11cb0ef41Sopenharmony_ci# -*- coding: utf-8 -*-
21cb0ef41Sopenharmony_ci"""Extension API for adding custom tags and behavior."""
31cb0ef41Sopenharmony_ciimport pprint
41cb0ef41Sopenharmony_ciimport re
51cb0ef41Sopenharmony_cifrom sys import version_info
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_cifrom markupsafe import Markup
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_cifrom . import nodes
101cb0ef41Sopenharmony_cifrom ._compat import iteritems
111cb0ef41Sopenharmony_cifrom ._compat import string_types
121cb0ef41Sopenharmony_cifrom ._compat import with_metaclass
131cb0ef41Sopenharmony_cifrom .defaults import BLOCK_END_STRING
141cb0ef41Sopenharmony_cifrom .defaults import BLOCK_START_STRING
151cb0ef41Sopenharmony_cifrom .defaults import COMMENT_END_STRING
161cb0ef41Sopenharmony_cifrom .defaults import COMMENT_START_STRING
171cb0ef41Sopenharmony_cifrom .defaults import KEEP_TRAILING_NEWLINE
181cb0ef41Sopenharmony_cifrom .defaults import LINE_COMMENT_PREFIX
191cb0ef41Sopenharmony_cifrom .defaults import LINE_STATEMENT_PREFIX
201cb0ef41Sopenharmony_cifrom .defaults import LSTRIP_BLOCKS
211cb0ef41Sopenharmony_cifrom .defaults import NEWLINE_SEQUENCE
221cb0ef41Sopenharmony_cifrom .defaults import TRIM_BLOCKS
231cb0ef41Sopenharmony_cifrom .defaults import VARIABLE_END_STRING
241cb0ef41Sopenharmony_cifrom .defaults import VARIABLE_START_STRING
251cb0ef41Sopenharmony_cifrom .environment import Environment
261cb0ef41Sopenharmony_cifrom .exceptions import TemplateAssertionError
271cb0ef41Sopenharmony_cifrom .exceptions import TemplateSyntaxError
281cb0ef41Sopenharmony_cifrom .nodes import ContextReference
291cb0ef41Sopenharmony_cifrom .runtime import concat
301cb0ef41Sopenharmony_cifrom .utils import contextfunction
311cb0ef41Sopenharmony_cifrom .utils import import_string
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci# the only real useful gettext functions for a Jinja template.  Note
341cb0ef41Sopenharmony_ci# that ugettext must be assigned to gettext as Jinja doesn't support
351cb0ef41Sopenharmony_ci# non unicode strings.
361cb0ef41Sopenharmony_ciGETTEXT_FUNCTIONS = ("_", "gettext", "ngettext")
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci_ws_re = re.compile(r"\s*\n\s*")
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ciclass ExtensionRegistry(type):
421cb0ef41Sopenharmony_ci    """Gives the extension an unique identifier."""
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci    def __new__(mcs, name, bases, d):
451cb0ef41Sopenharmony_ci        rv = type.__new__(mcs, name, bases, d)
461cb0ef41Sopenharmony_ci        rv.identifier = rv.__module__ + "." + rv.__name__
471cb0ef41Sopenharmony_ci        return rv
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ciclass Extension(with_metaclass(ExtensionRegistry, object)):
511cb0ef41Sopenharmony_ci    """Extensions can be used to add extra functionality to the Jinja template
521cb0ef41Sopenharmony_ci    system at the parser level.  Custom extensions are bound to an environment
531cb0ef41Sopenharmony_ci    but may not store environment specific data on `self`.  The reason for
541cb0ef41Sopenharmony_ci    this is that an extension can be bound to another environment (for
551cb0ef41Sopenharmony_ci    overlays) by creating a copy and reassigning the `environment` attribute.
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci    As extensions are created by the environment they cannot accept any
581cb0ef41Sopenharmony_ci    arguments for configuration.  One may want to work around that by using
591cb0ef41Sopenharmony_ci    a factory function, but that is not possible as extensions are identified
601cb0ef41Sopenharmony_ci    by their import name.  The correct way to configure the extension is
611cb0ef41Sopenharmony_ci    storing the configuration values on the environment.  Because this way the
621cb0ef41Sopenharmony_ci    environment ends up acting as central configuration storage the
631cb0ef41Sopenharmony_ci    attributes may clash which is why extensions have to ensure that the names
641cb0ef41Sopenharmony_ci    they choose for configuration are not too generic.  ``prefix`` for example
651cb0ef41Sopenharmony_ci    is a terrible name, ``fragment_cache_prefix`` on the other hand is a good
661cb0ef41Sopenharmony_ci    name as includes the name of the extension (fragment cache).
671cb0ef41Sopenharmony_ci    """
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ci    #: if this extension parses this is the list of tags it's listening to.
701cb0ef41Sopenharmony_ci    tags = set()
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci    #: the priority of that extension.  This is especially useful for
731cb0ef41Sopenharmony_ci    #: extensions that preprocess values.  A lower value means higher
741cb0ef41Sopenharmony_ci    #: priority.
751cb0ef41Sopenharmony_ci    #:
761cb0ef41Sopenharmony_ci    #: .. versionadded:: 2.4
771cb0ef41Sopenharmony_ci    priority = 100
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci    def __init__(self, environment):
801cb0ef41Sopenharmony_ci        self.environment = environment
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci    def bind(self, environment):
831cb0ef41Sopenharmony_ci        """Create a copy of this extension bound to another environment."""
841cb0ef41Sopenharmony_ci        rv = object.__new__(self.__class__)
851cb0ef41Sopenharmony_ci        rv.__dict__.update(self.__dict__)
861cb0ef41Sopenharmony_ci        rv.environment = environment
871cb0ef41Sopenharmony_ci        return rv
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci    def preprocess(self, source, name, filename=None):
901cb0ef41Sopenharmony_ci        """This method is called before the actual lexing and can be used to
911cb0ef41Sopenharmony_ci        preprocess the source.  The `filename` is optional.  The return value
921cb0ef41Sopenharmony_ci        must be the preprocessed source.
931cb0ef41Sopenharmony_ci        """
941cb0ef41Sopenharmony_ci        return source
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci    def filter_stream(self, stream):
971cb0ef41Sopenharmony_ci        """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used
981cb0ef41Sopenharmony_ci        to filter tokens returned.  This method has to return an iterable of
991cb0ef41Sopenharmony_ci        :class:`~jinja2.lexer.Token`\\s, but it doesn't have to return a
1001cb0ef41Sopenharmony_ci        :class:`~jinja2.lexer.TokenStream`.
1011cb0ef41Sopenharmony_ci        """
1021cb0ef41Sopenharmony_ci        return stream
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci    def parse(self, parser):
1051cb0ef41Sopenharmony_ci        """If any of the :attr:`tags` matched this method is called with the
1061cb0ef41Sopenharmony_ci        parser as first argument.  The token the parser stream is pointing at
1071cb0ef41Sopenharmony_ci        is the name token that matched.  This method has to return one or a
1081cb0ef41Sopenharmony_ci        list of multiple nodes.
1091cb0ef41Sopenharmony_ci        """
1101cb0ef41Sopenharmony_ci        raise NotImplementedError()
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_ci    def attr(self, name, lineno=None):
1131cb0ef41Sopenharmony_ci        """Return an attribute node for the current extension.  This is useful
1141cb0ef41Sopenharmony_ci        to pass constants on extensions to generated template code.
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci        ::
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_ci            self.attr('_my_attribute', lineno=lineno)
1191cb0ef41Sopenharmony_ci        """
1201cb0ef41Sopenharmony_ci        return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci    def call_method(
1231cb0ef41Sopenharmony_ci        self, name, args=None, kwargs=None, dyn_args=None, dyn_kwargs=None, lineno=None
1241cb0ef41Sopenharmony_ci    ):
1251cb0ef41Sopenharmony_ci        """Call a method of the extension.  This is a shortcut for
1261cb0ef41Sopenharmony_ci        :meth:`attr` + :class:`jinja2.nodes.Call`.
1271cb0ef41Sopenharmony_ci        """
1281cb0ef41Sopenharmony_ci        if args is None:
1291cb0ef41Sopenharmony_ci            args = []
1301cb0ef41Sopenharmony_ci        if kwargs is None:
1311cb0ef41Sopenharmony_ci            kwargs = []
1321cb0ef41Sopenharmony_ci        return nodes.Call(
1331cb0ef41Sopenharmony_ci            self.attr(name, lineno=lineno),
1341cb0ef41Sopenharmony_ci            args,
1351cb0ef41Sopenharmony_ci            kwargs,
1361cb0ef41Sopenharmony_ci            dyn_args,
1371cb0ef41Sopenharmony_ci            dyn_kwargs,
1381cb0ef41Sopenharmony_ci            lineno=lineno,
1391cb0ef41Sopenharmony_ci        )
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci@contextfunction
1431cb0ef41Sopenharmony_cidef _gettext_alias(__context, *args, **kwargs):
1441cb0ef41Sopenharmony_ci    return __context.call(__context.resolve("gettext"), *args, **kwargs)
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_cidef _make_new_gettext(func):
1481cb0ef41Sopenharmony_ci    @contextfunction
1491cb0ef41Sopenharmony_ci    def gettext(__context, __string, **variables):
1501cb0ef41Sopenharmony_ci        rv = __context.call(func, __string)
1511cb0ef41Sopenharmony_ci        if __context.eval_ctx.autoescape:
1521cb0ef41Sopenharmony_ci            rv = Markup(rv)
1531cb0ef41Sopenharmony_ci        # Always treat as a format string, even if there are no
1541cb0ef41Sopenharmony_ci        # variables. This makes translation strings more consistent
1551cb0ef41Sopenharmony_ci        # and predictable. This requires escaping
1561cb0ef41Sopenharmony_ci        return rv % variables
1571cb0ef41Sopenharmony_ci
1581cb0ef41Sopenharmony_ci    return gettext
1591cb0ef41Sopenharmony_ci
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_cidef _make_new_ngettext(func):
1621cb0ef41Sopenharmony_ci    @contextfunction
1631cb0ef41Sopenharmony_ci    def ngettext(__context, __singular, __plural, __num, **variables):
1641cb0ef41Sopenharmony_ci        variables.setdefault("num", __num)
1651cb0ef41Sopenharmony_ci        rv = __context.call(func, __singular, __plural, __num)
1661cb0ef41Sopenharmony_ci        if __context.eval_ctx.autoescape:
1671cb0ef41Sopenharmony_ci            rv = Markup(rv)
1681cb0ef41Sopenharmony_ci        # Always treat as a format string, see gettext comment above.
1691cb0ef41Sopenharmony_ci        return rv % variables
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_ci    return ngettext
1721cb0ef41Sopenharmony_ci
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ciclass InternationalizationExtension(Extension):
1751cb0ef41Sopenharmony_ci    """This extension adds gettext support to Jinja."""
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci    tags = {"trans"}
1781cb0ef41Sopenharmony_ci
1791cb0ef41Sopenharmony_ci    # TODO: the i18n extension is currently reevaluating values in a few
1801cb0ef41Sopenharmony_ci    # situations.  Take this example:
1811cb0ef41Sopenharmony_ci    #   {% trans count=something() %}{{ count }} foo{% pluralize
1821cb0ef41Sopenharmony_ci    #     %}{{ count }} fooss{% endtrans %}
1831cb0ef41Sopenharmony_ci    # something is called twice here.  One time for the gettext value and
1841cb0ef41Sopenharmony_ci    # the other time for the n-parameter of the ngettext function.
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_ci    def __init__(self, environment):
1871cb0ef41Sopenharmony_ci        Extension.__init__(self, environment)
1881cb0ef41Sopenharmony_ci        environment.globals["_"] = _gettext_alias
1891cb0ef41Sopenharmony_ci        environment.extend(
1901cb0ef41Sopenharmony_ci            install_gettext_translations=self._install,
1911cb0ef41Sopenharmony_ci            install_null_translations=self._install_null,
1921cb0ef41Sopenharmony_ci            install_gettext_callables=self._install_callables,
1931cb0ef41Sopenharmony_ci            uninstall_gettext_translations=self._uninstall,
1941cb0ef41Sopenharmony_ci            extract_translations=self._extract,
1951cb0ef41Sopenharmony_ci            newstyle_gettext=False,
1961cb0ef41Sopenharmony_ci        )
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci    def _install(self, translations, newstyle=None):
1991cb0ef41Sopenharmony_ci        gettext = getattr(translations, "ugettext", None)
2001cb0ef41Sopenharmony_ci        if gettext is None:
2011cb0ef41Sopenharmony_ci            gettext = translations.gettext
2021cb0ef41Sopenharmony_ci        ngettext = getattr(translations, "ungettext", None)
2031cb0ef41Sopenharmony_ci        if ngettext is None:
2041cb0ef41Sopenharmony_ci            ngettext = translations.ngettext
2051cb0ef41Sopenharmony_ci        self._install_callables(gettext, ngettext, newstyle)
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci    def _install_null(self, newstyle=None):
2081cb0ef41Sopenharmony_ci        self._install_callables(
2091cb0ef41Sopenharmony_ci            lambda x: x, lambda s, p, n: (n != 1 and (p,) or (s,))[0], newstyle
2101cb0ef41Sopenharmony_ci        )
2111cb0ef41Sopenharmony_ci
2121cb0ef41Sopenharmony_ci    def _install_callables(self, gettext, ngettext, newstyle=None):
2131cb0ef41Sopenharmony_ci        if newstyle is not None:
2141cb0ef41Sopenharmony_ci            self.environment.newstyle_gettext = newstyle
2151cb0ef41Sopenharmony_ci        if self.environment.newstyle_gettext:
2161cb0ef41Sopenharmony_ci            gettext = _make_new_gettext(gettext)
2171cb0ef41Sopenharmony_ci            ngettext = _make_new_ngettext(ngettext)
2181cb0ef41Sopenharmony_ci        self.environment.globals.update(gettext=gettext, ngettext=ngettext)
2191cb0ef41Sopenharmony_ci
2201cb0ef41Sopenharmony_ci    def _uninstall(self, translations):
2211cb0ef41Sopenharmony_ci        for key in "gettext", "ngettext":
2221cb0ef41Sopenharmony_ci            self.environment.globals.pop(key, None)
2231cb0ef41Sopenharmony_ci
2241cb0ef41Sopenharmony_ci    def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
2251cb0ef41Sopenharmony_ci        if isinstance(source, string_types):
2261cb0ef41Sopenharmony_ci            source = self.environment.parse(source)
2271cb0ef41Sopenharmony_ci        return extract_from_ast(source, gettext_functions)
2281cb0ef41Sopenharmony_ci
2291cb0ef41Sopenharmony_ci    def parse(self, parser):
2301cb0ef41Sopenharmony_ci        """Parse a translatable tag."""
2311cb0ef41Sopenharmony_ci        lineno = next(parser.stream).lineno
2321cb0ef41Sopenharmony_ci        num_called_num = False
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ci        # find all the variables referenced.  Additionally a variable can be
2351cb0ef41Sopenharmony_ci        # defined in the body of the trans block too, but this is checked at
2361cb0ef41Sopenharmony_ci        # a later state.
2371cb0ef41Sopenharmony_ci        plural_expr = None
2381cb0ef41Sopenharmony_ci        plural_expr_assignment = None
2391cb0ef41Sopenharmony_ci        variables = {}
2401cb0ef41Sopenharmony_ci        trimmed = None
2411cb0ef41Sopenharmony_ci        while parser.stream.current.type != "block_end":
2421cb0ef41Sopenharmony_ci            if variables:
2431cb0ef41Sopenharmony_ci                parser.stream.expect("comma")
2441cb0ef41Sopenharmony_ci
2451cb0ef41Sopenharmony_ci            # skip colon for python compatibility
2461cb0ef41Sopenharmony_ci            if parser.stream.skip_if("colon"):
2471cb0ef41Sopenharmony_ci                break
2481cb0ef41Sopenharmony_ci
2491cb0ef41Sopenharmony_ci            name = parser.stream.expect("name")
2501cb0ef41Sopenharmony_ci            if name.value in variables:
2511cb0ef41Sopenharmony_ci                parser.fail(
2521cb0ef41Sopenharmony_ci                    "translatable variable %r defined twice." % name.value,
2531cb0ef41Sopenharmony_ci                    name.lineno,
2541cb0ef41Sopenharmony_ci                    exc=TemplateAssertionError,
2551cb0ef41Sopenharmony_ci                )
2561cb0ef41Sopenharmony_ci
2571cb0ef41Sopenharmony_ci            # expressions
2581cb0ef41Sopenharmony_ci            if parser.stream.current.type == "assign":
2591cb0ef41Sopenharmony_ci                next(parser.stream)
2601cb0ef41Sopenharmony_ci                variables[name.value] = var = parser.parse_expression()
2611cb0ef41Sopenharmony_ci            elif trimmed is None and name.value in ("trimmed", "notrimmed"):
2621cb0ef41Sopenharmony_ci                trimmed = name.value == "trimmed"
2631cb0ef41Sopenharmony_ci                continue
2641cb0ef41Sopenharmony_ci            else:
2651cb0ef41Sopenharmony_ci                variables[name.value] = var = nodes.Name(name.value, "load")
2661cb0ef41Sopenharmony_ci
2671cb0ef41Sopenharmony_ci            if plural_expr is None:
2681cb0ef41Sopenharmony_ci                if isinstance(var, nodes.Call):
2691cb0ef41Sopenharmony_ci                    plural_expr = nodes.Name("_trans", "load")
2701cb0ef41Sopenharmony_ci                    variables[name.value] = plural_expr
2711cb0ef41Sopenharmony_ci                    plural_expr_assignment = nodes.Assign(
2721cb0ef41Sopenharmony_ci                        nodes.Name("_trans", "store"), var
2731cb0ef41Sopenharmony_ci                    )
2741cb0ef41Sopenharmony_ci                else:
2751cb0ef41Sopenharmony_ci                    plural_expr = var
2761cb0ef41Sopenharmony_ci                num_called_num = name.value == "num"
2771cb0ef41Sopenharmony_ci
2781cb0ef41Sopenharmony_ci        parser.stream.expect("block_end")
2791cb0ef41Sopenharmony_ci
2801cb0ef41Sopenharmony_ci        plural = None
2811cb0ef41Sopenharmony_ci        have_plural = False
2821cb0ef41Sopenharmony_ci        referenced = set()
2831cb0ef41Sopenharmony_ci
2841cb0ef41Sopenharmony_ci        # now parse until endtrans or pluralize
2851cb0ef41Sopenharmony_ci        singular_names, singular = self._parse_block(parser, True)
2861cb0ef41Sopenharmony_ci        if singular_names:
2871cb0ef41Sopenharmony_ci            referenced.update(singular_names)
2881cb0ef41Sopenharmony_ci            if plural_expr is None:
2891cb0ef41Sopenharmony_ci                plural_expr = nodes.Name(singular_names[0], "load")
2901cb0ef41Sopenharmony_ci                num_called_num = singular_names[0] == "num"
2911cb0ef41Sopenharmony_ci
2921cb0ef41Sopenharmony_ci        # if we have a pluralize block, we parse that too
2931cb0ef41Sopenharmony_ci        if parser.stream.current.test("name:pluralize"):
2941cb0ef41Sopenharmony_ci            have_plural = True
2951cb0ef41Sopenharmony_ci            next(parser.stream)
2961cb0ef41Sopenharmony_ci            if parser.stream.current.type != "block_end":
2971cb0ef41Sopenharmony_ci                name = parser.stream.expect("name")
2981cb0ef41Sopenharmony_ci                if name.value not in variables:
2991cb0ef41Sopenharmony_ci                    parser.fail(
3001cb0ef41Sopenharmony_ci                        "unknown variable %r for pluralization" % name.value,
3011cb0ef41Sopenharmony_ci                        name.lineno,
3021cb0ef41Sopenharmony_ci                        exc=TemplateAssertionError,
3031cb0ef41Sopenharmony_ci                    )
3041cb0ef41Sopenharmony_ci                plural_expr = variables[name.value]
3051cb0ef41Sopenharmony_ci                num_called_num = name.value == "num"
3061cb0ef41Sopenharmony_ci            parser.stream.expect("block_end")
3071cb0ef41Sopenharmony_ci            plural_names, plural = self._parse_block(parser, False)
3081cb0ef41Sopenharmony_ci            next(parser.stream)
3091cb0ef41Sopenharmony_ci            referenced.update(plural_names)
3101cb0ef41Sopenharmony_ci        else:
3111cb0ef41Sopenharmony_ci            next(parser.stream)
3121cb0ef41Sopenharmony_ci
3131cb0ef41Sopenharmony_ci        # register free names as simple name expressions
3141cb0ef41Sopenharmony_ci        for var in referenced:
3151cb0ef41Sopenharmony_ci            if var not in variables:
3161cb0ef41Sopenharmony_ci                variables[var] = nodes.Name(var, "load")
3171cb0ef41Sopenharmony_ci
3181cb0ef41Sopenharmony_ci        if not have_plural:
3191cb0ef41Sopenharmony_ci            plural_expr = None
3201cb0ef41Sopenharmony_ci        elif plural_expr is None:
3211cb0ef41Sopenharmony_ci            parser.fail("pluralize without variables", lineno)
3221cb0ef41Sopenharmony_ci
3231cb0ef41Sopenharmony_ci        if trimmed is None:
3241cb0ef41Sopenharmony_ci            trimmed = self.environment.policies["ext.i18n.trimmed"]
3251cb0ef41Sopenharmony_ci        if trimmed:
3261cb0ef41Sopenharmony_ci            singular = self._trim_whitespace(singular)
3271cb0ef41Sopenharmony_ci            if plural:
3281cb0ef41Sopenharmony_ci                plural = self._trim_whitespace(plural)
3291cb0ef41Sopenharmony_ci
3301cb0ef41Sopenharmony_ci        node = self._make_node(
3311cb0ef41Sopenharmony_ci            singular,
3321cb0ef41Sopenharmony_ci            plural,
3331cb0ef41Sopenharmony_ci            variables,
3341cb0ef41Sopenharmony_ci            plural_expr,
3351cb0ef41Sopenharmony_ci            bool(referenced),
3361cb0ef41Sopenharmony_ci            num_called_num and have_plural,
3371cb0ef41Sopenharmony_ci        )
3381cb0ef41Sopenharmony_ci        node.set_lineno(lineno)
3391cb0ef41Sopenharmony_ci        if plural_expr_assignment is not None:
3401cb0ef41Sopenharmony_ci            return [plural_expr_assignment, node]
3411cb0ef41Sopenharmony_ci        else:
3421cb0ef41Sopenharmony_ci            return node
3431cb0ef41Sopenharmony_ci
3441cb0ef41Sopenharmony_ci    def _trim_whitespace(self, string, _ws_re=_ws_re):
3451cb0ef41Sopenharmony_ci        return _ws_re.sub(" ", string.strip())
3461cb0ef41Sopenharmony_ci
3471cb0ef41Sopenharmony_ci    def _parse_block(self, parser, allow_pluralize):
3481cb0ef41Sopenharmony_ci        """Parse until the next block tag with a given name."""
3491cb0ef41Sopenharmony_ci        referenced = []
3501cb0ef41Sopenharmony_ci        buf = []
3511cb0ef41Sopenharmony_ci        while 1:
3521cb0ef41Sopenharmony_ci            if parser.stream.current.type == "data":
3531cb0ef41Sopenharmony_ci                buf.append(parser.stream.current.value.replace("%", "%%"))
3541cb0ef41Sopenharmony_ci                next(parser.stream)
3551cb0ef41Sopenharmony_ci            elif parser.stream.current.type == "variable_begin":
3561cb0ef41Sopenharmony_ci                next(parser.stream)
3571cb0ef41Sopenharmony_ci                name = parser.stream.expect("name").value
3581cb0ef41Sopenharmony_ci                referenced.append(name)
3591cb0ef41Sopenharmony_ci                buf.append("%%(%s)s" % name)
3601cb0ef41Sopenharmony_ci                parser.stream.expect("variable_end")
3611cb0ef41Sopenharmony_ci            elif parser.stream.current.type == "block_begin":
3621cb0ef41Sopenharmony_ci                next(parser.stream)
3631cb0ef41Sopenharmony_ci                if parser.stream.current.test("name:endtrans"):
3641cb0ef41Sopenharmony_ci                    break
3651cb0ef41Sopenharmony_ci                elif parser.stream.current.test("name:pluralize"):
3661cb0ef41Sopenharmony_ci                    if allow_pluralize:
3671cb0ef41Sopenharmony_ci                        break
3681cb0ef41Sopenharmony_ci                    parser.fail(
3691cb0ef41Sopenharmony_ci                        "a translatable section can have only one pluralize section"
3701cb0ef41Sopenharmony_ci                    )
3711cb0ef41Sopenharmony_ci                parser.fail(
3721cb0ef41Sopenharmony_ci                    "control structures in translatable sections are not allowed"
3731cb0ef41Sopenharmony_ci                )
3741cb0ef41Sopenharmony_ci            elif parser.stream.eos:
3751cb0ef41Sopenharmony_ci                parser.fail("unclosed translation block")
3761cb0ef41Sopenharmony_ci            else:
3771cb0ef41Sopenharmony_ci                raise RuntimeError("internal parser error")
3781cb0ef41Sopenharmony_ci
3791cb0ef41Sopenharmony_ci        return referenced, concat(buf)
3801cb0ef41Sopenharmony_ci
3811cb0ef41Sopenharmony_ci    def _make_node(
3821cb0ef41Sopenharmony_ci        self, singular, plural, variables, plural_expr, vars_referenced, num_called_num
3831cb0ef41Sopenharmony_ci    ):
3841cb0ef41Sopenharmony_ci        """Generates a useful node from the data provided."""
3851cb0ef41Sopenharmony_ci        # no variables referenced?  no need to escape for old style
3861cb0ef41Sopenharmony_ci        # gettext invocations only if there are vars.
3871cb0ef41Sopenharmony_ci        if not vars_referenced and not self.environment.newstyle_gettext:
3881cb0ef41Sopenharmony_ci            singular = singular.replace("%%", "%")
3891cb0ef41Sopenharmony_ci            if plural:
3901cb0ef41Sopenharmony_ci                plural = plural.replace("%%", "%")
3911cb0ef41Sopenharmony_ci
3921cb0ef41Sopenharmony_ci        # singular only:
3931cb0ef41Sopenharmony_ci        if plural_expr is None:
3941cb0ef41Sopenharmony_ci            gettext = nodes.Name("gettext", "load")
3951cb0ef41Sopenharmony_ci            node = nodes.Call(gettext, [nodes.Const(singular)], [], None, None)
3961cb0ef41Sopenharmony_ci
3971cb0ef41Sopenharmony_ci        # singular and plural
3981cb0ef41Sopenharmony_ci        else:
3991cb0ef41Sopenharmony_ci            ngettext = nodes.Name("ngettext", "load")
4001cb0ef41Sopenharmony_ci            node = nodes.Call(
4011cb0ef41Sopenharmony_ci                ngettext,
4021cb0ef41Sopenharmony_ci                [nodes.Const(singular), nodes.Const(plural), plural_expr],
4031cb0ef41Sopenharmony_ci                [],
4041cb0ef41Sopenharmony_ci                None,
4051cb0ef41Sopenharmony_ci                None,
4061cb0ef41Sopenharmony_ci            )
4071cb0ef41Sopenharmony_ci
4081cb0ef41Sopenharmony_ci        # in case newstyle gettext is used, the method is powerful
4091cb0ef41Sopenharmony_ci        # enough to handle the variable expansion and autoescape
4101cb0ef41Sopenharmony_ci        # handling itself
4111cb0ef41Sopenharmony_ci        if self.environment.newstyle_gettext:
4121cb0ef41Sopenharmony_ci            for key, value in iteritems(variables):
4131cb0ef41Sopenharmony_ci                # the function adds that later anyways in case num was
4141cb0ef41Sopenharmony_ci                # called num, so just skip it.
4151cb0ef41Sopenharmony_ci                if num_called_num and key == "num":
4161cb0ef41Sopenharmony_ci                    continue
4171cb0ef41Sopenharmony_ci                node.kwargs.append(nodes.Keyword(key, value))
4181cb0ef41Sopenharmony_ci
4191cb0ef41Sopenharmony_ci        # otherwise do that here
4201cb0ef41Sopenharmony_ci        else:
4211cb0ef41Sopenharmony_ci            # mark the return value as safe if we are in an
4221cb0ef41Sopenharmony_ci            # environment with autoescaping turned on
4231cb0ef41Sopenharmony_ci            node = nodes.MarkSafeIfAutoescape(node)
4241cb0ef41Sopenharmony_ci            if variables:
4251cb0ef41Sopenharmony_ci                node = nodes.Mod(
4261cb0ef41Sopenharmony_ci                    node,
4271cb0ef41Sopenharmony_ci                    nodes.Dict(
4281cb0ef41Sopenharmony_ci                        [
4291cb0ef41Sopenharmony_ci                            nodes.Pair(nodes.Const(key), value)
4301cb0ef41Sopenharmony_ci                            for key, value in variables.items()
4311cb0ef41Sopenharmony_ci                        ]
4321cb0ef41Sopenharmony_ci                    ),
4331cb0ef41Sopenharmony_ci                )
4341cb0ef41Sopenharmony_ci        return nodes.Output([node])
4351cb0ef41Sopenharmony_ci
4361cb0ef41Sopenharmony_ci
4371cb0ef41Sopenharmony_ciclass ExprStmtExtension(Extension):
4381cb0ef41Sopenharmony_ci    """Adds a `do` tag to Jinja that works like the print statement just
4391cb0ef41Sopenharmony_ci    that it doesn't print the return value.
4401cb0ef41Sopenharmony_ci    """
4411cb0ef41Sopenharmony_ci
4421cb0ef41Sopenharmony_ci    tags = set(["do"])
4431cb0ef41Sopenharmony_ci
4441cb0ef41Sopenharmony_ci    def parse(self, parser):
4451cb0ef41Sopenharmony_ci        node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
4461cb0ef41Sopenharmony_ci        node.node = parser.parse_tuple()
4471cb0ef41Sopenharmony_ci        return node
4481cb0ef41Sopenharmony_ci
4491cb0ef41Sopenharmony_ci
4501cb0ef41Sopenharmony_ciclass LoopControlExtension(Extension):
4511cb0ef41Sopenharmony_ci    """Adds break and continue to the template engine."""
4521cb0ef41Sopenharmony_ci
4531cb0ef41Sopenharmony_ci    tags = set(["break", "continue"])
4541cb0ef41Sopenharmony_ci
4551cb0ef41Sopenharmony_ci    def parse(self, parser):
4561cb0ef41Sopenharmony_ci        token = next(parser.stream)
4571cb0ef41Sopenharmony_ci        if token.value == "break":
4581cb0ef41Sopenharmony_ci            return nodes.Break(lineno=token.lineno)
4591cb0ef41Sopenharmony_ci        return nodes.Continue(lineno=token.lineno)
4601cb0ef41Sopenharmony_ci
4611cb0ef41Sopenharmony_ci
4621cb0ef41Sopenharmony_ciclass WithExtension(Extension):
4631cb0ef41Sopenharmony_ci    pass
4641cb0ef41Sopenharmony_ci
4651cb0ef41Sopenharmony_ci
4661cb0ef41Sopenharmony_ciclass AutoEscapeExtension(Extension):
4671cb0ef41Sopenharmony_ci    pass
4681cb0ef41Sopenharmony_ci
4691cb0ef41Sopenharmony_ci
4701cb0ef41Sopenharmony_ciclass DebugExtension(Extension):
4711cb0ef41Sopenharmony_ci    """A ``{% debug %}`` tag that dumps the available variables,
4721cb0ef41Sopenharmony_ci    filters, and tests.
4731cb0ef41Sopenharmony_ci
4741cb0ef41Sopenharmony_ci    .. code-block:: html+jinja
4751cb0ef41Sopenharmony_ci
4761cb0ef41Sopenharmony_ci        <pre>{% debug %}</pre>
4771cb0ef41Sopenharmony_ci
4781cb0ef41Sopenharmony_ci    .. code-block:: text
4791cb0ef41Sopenharmony_ci
4801cb0ef41Sopenharmony_ci        {'context': {'cycler': <class 'jinja2.utils.Cycler'>,
4811cb0ef41Sopenharmony_ci                     ...,
4821cb0ef41Sopenharmony_ci                     'namespace': <class 'jinja2.utils.Namespace'>},
4831cb0ef41Sopenharmony_ci         'filters': ['abs', 'attr', 'batch', 'capitalize', 'center', 'count', 'd',
4841cb0ef41Sopenharmony_ci                     ..., 'urlencode', 'urlize', 'wordcount', 'wordwrap', 'xmlattr'],
4851cb0ef41Sopenharmony_ci         'tests': ['!=', '<', '<=', '==', '>', '>=', 'callable', 'defined',
4861cb0ef41Sopenharmony_ci                   ..., 'odd', 'sameas', 'sequence', 'string', 'undefined', 'upper']}
4871cb0ef41Sopenharmony_ci
4881cb0ef41Sopenharmony_ci    .. versionadded:: 2.11.0
4891cb0ef41Sopenharmony_ci    """
4901cb0ef41Sopenharmony_ci
4911cb0ef41Sopenharmony_ci    tags = {"debug"}
4921cb0ef41Sopenharmony_ci
4931cb0ef41Sopenharmony_ci    def parse(self, parser):
4941cb0ef41Sopenharmony_ci        lineno = parser.stream.expect("name:debug").lineno
4951cb0ef41Sopenharmony_ci        context = ContextReference()
4961cb0ef41Sopenharmony_ci        result = self.call_method("_render", [context], lineno=lineno)
4971cb0ef41Sopenharmony_ci        return nodes.Output([result], lineno=lineno)
4981cb0ef41Sopenharmony_ci
4991cb0ef41Sopenharmony_ci    def _render(self, context):
5001cb0ef41Sopenharmony_ci        result = {
5011cb0ef41Sopenharmony_ci            "context": context.get_all(),
5021cb0ef41Sopenharmony_ci            "filters": sorted(self.environment.filters.keys()),
5031cb0ef41Sopenharmony_ci            "tests": sorted(self.environment.tests.keys()),
5041cb0ef41Sopenharmony_ci        }
5051cb0ef41Sopenharmony_ci
5061cb0ef41Sopenharmony_ci        # Set the depth since the intent is to show the top few names.
5071cb0ef41Sopenharmony_ci        if version_info[:2] >= (3, 4):
5081cb0ef41Sopenharmony_ci            return pprint.pformat(result, depth=3, compact=True)
5091cb0ef41Sopenharmony_ci        else:
5101cb0ef41Sopenharmony_ci            return pprint.pformat(result, depth=3)
5111cb0ef41Sopenharmony_ci
5121cb0ef41Sopenharmony_ci
5131cb0ef41Sopenharmony_cidef extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, babel_style=True):
5141cb0ef41Sopenharmony_ci    """Extract localizable strings from the given template node.  Per
5151cb0ef41Sopenharmony_ci    default this function returns matches in babel style that means non string
5161cb0ef41Sopenharmony_ci    parameters as well as keyword arguments are returned as `None`.  This
5171cb0ef41Sopenharmony_ci    allows Babel to figure out what you really meant if you are using
5181cb0ef41Sopenharmony_ci    gettext functions that allow keyword arguments for placeholder expansion.
5191cb0ef41Sopenharmony_ci    If you don't want that behavior set the `babel_style` parameter to `False`
5201cb0ef41Sopenharmony_ci    which causes only strings to be returned and parameters are always stored
5211cb0ef41Sopenharmony_ci    in tuples.  As a consequence invalid gettext calls (calls without a single
5221cb0ef41Sopenharmony_ci    string parameter or string parameters after non-string parameters) are
5231cb0ef41Sopenharmony_ci    skipped.
5241cb0ef41Sopenharmony_ci
5251cb0ef41Sopenharmony_ci    This example explains the behavior:
5261cb0ef41Sopenharmony_ci
5271cb0ef41Sopenharmony_ci    >>> from jinja2 import Environment
5281cb0ef41Sopenharmony_ci    >>> env = Environment()
5291cb0ef41Sopenharmony_ci    >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}')
5301cb0ef41Sopenharmony_ci    >>> list(extract_from_ast(node))
5311cb0ef41Sopenharmony_ci    [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))]
5321cb0ef41Sopenharmony_ci    >>> list(extract_from_ast(node, babel_style=False))
5331cb0ef41Sopenharmony_ci    [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))]
5341cb0ef41Sopenharmony_ci
5351cb0ef41Sopenharmony_ci    For every string found this function yields a ``(lineno, function,
5361cb0ef41Sopenharmony_ci    message)`` tuple, where:
5371cb0ef41Sopenharmony_ci
5381cb0ef41Sopenharmony_ci    * ``lineno`` is the number of the line on which the string was found,
5391cb0ef41Sopenharmony_ci    * ``function`` is the name of the ``gettext`` function used (if the
5401cb0ef41Sopenharmony_ci      string was extracted from embedded Python code), and
5411cb0ef41Sopenharmony_ci    *  ``message`` is the string itself (a ``unicode`` object, or a tuple
5421cb0ef41Sopenharmony_ci       of ``unicode`` objects for functions with multiple string arguments).
5431cb0ef41Sopenharmony_ci
5441cb0ef41Sopenharmony_ci    This extraction function operates on the AST and is because of that unable
5451cb0ef41Sopenharmony_ci    to extract any comments.  For comment support you have to use the babel
5461cb0ef41Sopenharmony_ci    extraction interface or extract comments yourself.
5471cb0ef41Sopenharmony_ci    """
5481cb0ef41Sopenharmony_ci    for node in node.find_all(nodes.Call):
5491cb0ef41Sopenharmony_ci        if (
5501cb0ef41Sopenharmony_ci            not isinstance(node.node, nodes.Name)
5511cb0ef41Sopenharmony_ci            or node.node.name not in gettext_functions
5521cb0ef41Sopenharmony_ci        ):
5531cb0ef41Sopenharmony_ci            continue
5541cb0ef41Sopenharmony_ci
5551cb0ef41Sopenharmony_ci        strings = []
5561cb0ef41Sopenharmony_ci        for arg in node.args:
5571cb0ef41Sopenharmony_ci            if isinstance(arg, nodes.Const) and isinstance(arg.value, string_types):
5581cb0ef41Sopenharmony_ci                strings.append(arg.value)
5591cb0ef41Sopenharmony_ci            else:
5601cb0ef41Sopenharmony_ci                strings.append(None)
5611cb0ef41Sopenharmony_ci
5621cb0ef41Sopenharmony_ci        for _ in node.kwargs:
5631cb0ef41Sopenharmony_ci            strings.append(None)
5641cb0ef41Sopenharmony_ci        if node.dyn_args is not None:
5651cb0ef41Sopenharmony_ci            strings.append(None)
5661cb0ef41Sopenharmony_ci        if node.dyn_kwargs is not None:
5671cb0ef41Sopenharmony_ci            strings.append(None)
5681cb0ef41Sopenharmony_ci
5691cb0ef41Sopenharmony_ci        if not babel_style:
5701cb0ef41Sopenharmony_ci            strings = tuple(x for x in strings if x is not None)
5711cb0ef41Sopenharmony_ci            if not strings:
5721cb0ef41Sopenharmony_ci                continue
5731cb0ef41Sopenharmony_ci        else:
5741cb0ef41Sopenharmony_ci            if len(strings) == 1:
5751cb0ef41Sopenharmony_ci                strings = strings[0]
5761cb0ef41Sopenharmony_ci            else:
5771cb0ef41Sopenharmony_ci                strings = tuple(strings)
5781cb0ef41Sopenharmony_ci        yield node.lineno, node.node.name, strings
5791cb0ef41Sopenharmony_ci
5801cb0ef41Sopenharmony_ci
5811cb0ef41Sopenharmony_ciclass _CommentFinder(object):
5821cb0ef41Sopenharmony_ci    """Helper class to find comments in a token stream.  Can only
5831cb0ef41Sopenharmony_ci    find comments for gettext calls forwards.  Once the comment
5841cb0ef41Sopenharmony_ci    from line 4 is found, a comment for line 1 will not return a
5851cb0ef41Sopenharmony_ci    usable value.
5861cb0ef41Sopenharmony_ci    """
5871cb0ef41Sopenharmony_ci
5881cb0ef41Sopenharmony_ci    def __init__(self, tokens, comment_tags):
5891cb0ef41Sopenharmony_ci        self.tokens = tokens
5901cb0ef41Sopenharmony_ci        self.comment_tags = comment_tags
5911cb0ef41Sopenharmony_ci        self.offset = 0
5921cb0ef41Sopenharmony_ci        self.last_lineno = 0
5931cb0ef41Sopenharmony_ci
5941cb0ef41Sopenharmony_ci    def find_backwards(self, offset):
5951cb0ef41Sopenharmony_ci        try:
5961cb0ef41Sopenharmony_ci            for _, token_type, token_value in reversed(
5971cb0ef41Sopenharmony_ci                self.tokens[self.offset : offset]
5981cb0ef41Sopenharmony_ci            ):
5991cb0ef41Sopenharmony_ci                if token_type in ("comment", "linecomment"):
6001cb0ef41Sopenharmony_ci                    try:
6011cb0ef41Sopenharmony_ci                        prefix, comment = token_value.split(None, 1)
6021cb0ef41Sopenharmony_ci                    except ValueError:
6031cb0ef41Sopenharmony_ci                        continue
6041cb0ef41Sopenharmony_ci                    if prefix in self.comment_tags:
6051cb0ef41Sopenharmony_ci                        return [comment.rstrip()]
6061cb0ef41Sopenharmony_ci            return []
6071cb0ef41Sopenharmony_ci        finally:
6081cb0ef41Sopenharmony_ci            self.offset = offset
6091cb0ef41Sopenharmony_ci
6101cb0ef41Sopenharmony_ci    def find_comments(self, lineno):
6111cb0ef41Sopenharmony_ci        if not self.comment_tags or self.last_lineno > lineno:
6121cb0ef41Sopenharmony_ci            return []
6131cb0ef41Sopenharmony_ci        for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset :]):
6141cb0ef41Sopenharmony_ci            if token_lineno > lineno:
6151cb0ef41Sopenharmony_ci                return self.find_backwards(self.offset + idx)
6161cb0ef41Sopenharmony_ci        return self.find_backwards(len(self.tokens))
6171cb0ef41Sopenharmony_ci
6181cb0ef41Sopenharmony_ci
6191cb0ef41Sopenharmony_cidef babel_extract(fileobj, keywords, comment_tags, options):
6201cb0ef41Sopenharmony_ci    """Babel extraction method for Jinja templates.
6211cb0ef41Sopenharmony_ci
6221cb0ef41Sopenharmony_ci    .. versionchanged:: 2.3
6231cb0ef41Sopenharmony_ci       Basic support for translation comments was added.  If `comment_tags`
6241cb0ef41Sopenharmony_ci       is now set to a list of keywords for extraction, the extractor will
6251cb0ef41Sopenharmony_ci       try to find the best preceding comment that begins with one of the
6261cb0ef41Sopenharmony_ci       keywords.  For best results, make sure to not have more than one
6271cb0ef41Sopenharmony_ci       gettext call in one line of code and the matching comment in the
6281cb0ef41Sopenharmony_ci       same line or the line before.
6291cb0ef41Sopenharmony_ci
6301cb0ef41Sopenharmony_ci    .. versionchanged:: 2.5.1
6311cb0ef41Sopenharmony_ci       The `newstyle_gettext` flag can be set to `True` to enable newstyle
6321cb0ef41Sopenharmony_ci       gettext calls.
6331cb0ef41Sopenharmony_ci
6341cb0ef41Sopenharmony_ci    .. versionchanged:: 2.7
6351cb0ef41Sopenharmony_ci       A `silent` option can now be provided.  If set to `False` template
6361cb0ef41Sopenharmony_ci       syntax errors are propagated instead of being ignored.
6371cb0ef41Sopenharmony_ci
6381cb0ef41Sopenharmony_ci    :param fileobj: the file-like object the messages should be extracted from
6391cb0ef41Sopenharmony_ci    :param keywords: a list of keywords (i.e. function names) that should be
6401cb0ef41Sopenharmony_ci                     recognized as translation functions
6411cb0ef41Sopenharmony_ci    :param comment_tags: a list of translator tags to search for and include
6421cb0ef41Sopenharmony_ci                         in the results.
6431cb0ef41Sopenharmony_ci    :param options: a dictionary of additional options (optional)
6441cb0ef41Sopenharmony_ci    :return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
6451cb0ef41Sopenharmony_ci             (comments will be empty currently)
6461cb0ef41Sopenharmony_ci    """
6471cb0ef41Sopenharmony_ci    extensions = set()
6481cb0ef41Sopenharmony_ci    for extension in options.get("extensions", "").split(","):
6491cb0ef41Sopenharmony_ci        extension = extension.strip()
6501cb0ef41Sopenharmony_ci        if not extension:
6511cb0ef41Sopenharmony_ci            continue
6521cb0ef41Sopenharmony_ci        extensions.add(import_string(extension))
6531cb0ef41Sopenharmony_ci    if InternationalizationExtension not in extensions:
6541cb0ef41Sopenharmony_ci        extensions.add(InternationalizationExtension)
6551cb0ef41Sopenharmony_ci
6561cb0ef41Sopenharmony_ci    def getbool(options, key, default=False):
6571cb0ef41Sopenharmony_ci        return options.get(key, str(default)).lower() in ("1", "on", "yes", "true")
6581cb0ef41Sopenharmony_ci
6591cb0ef41Sopenharmony_ci    silent = getbool(options, "silent", True)
6601cb0ef41Sopenharmony_ci    environment = Environment(
6611cb0ef41Sopenharmony_ci        options.get("block_start_string", BLOCK_START_STRING),
6621cb0ef41Sopenharmony_ci        options.get("block_end_string", BLOCK_END_STRING),
6631cb0ef41Sopenharmony_ci        options.get("variable_start_string", VARIABLE_START_STRING),
6641cb0ef41Sopenharmony_ci        options.get("variable_end_string", VARIABLE_END_STRING),
6651cb0ef41Sopenharmony_ci        options.get("comment_start_string", COMMENT_START_STRING),
6661cb0ef41Sopenharmony_ci        options.get("comment_end_string", COMMENT_END_STRING),
6671cb0ef41Sopenharmony_ci        options.get("line_statement_prefix") or LINE_STATEMENT_PREFIX,
6681cb0ef41Sopenharmony_ci        options.get("line_comment_prefix") or LINE_COMMENT_PREFIX,
6691cb0ef41Sopenharmony_ci        getbool(options, "trim_blocks", TRIM_BLOCKS),
6701cb0ef41Sopenharmony_ci        getbool(options, "lstrip_blocks", LSTRIP_BLOCKS),
6711cb0ef41Sopenharmony_ci        NEWLINE_SEQUENCE,
6721cb0ef41Sopenharmony_ci        getbool(options, "keep_trailing_newline", KEEP_TRAILING_NEWLINE),
6731cb0ef41Sopenharmony_ci        frozenset(extensions),
6741cb0ef41Sopenharmony_ci        cache_size=0,
6751cb0ef41Sopenharmony_ci        auto_reload=False,
6761cb0ef41Sopenharmony_ci    )
6771cb0ef41Sopenharmony_ci
6781cb0ef41Sopenharmony_ci    if getbool(options, "trimmed"):
6791cb0ef41Sopenharmony_ci        environment.policies["ext.i18n.trimmed"] = True
6801cb0ef41Sopenharmony_ci    if getbool(options, "newstyle_gettext"):
6811cb0ef41Sopenharmony_ci        environment.newstyle_gettext = True
6821cb0ef41Sopenharmony_ci
6831cb0ef41Sopenharmony_ci    source = fileobj.read().decode(options.get("encoding", "utf-8"))
6841cb0ef41Sopenharmony_ci    try:
6851cb0ef41Sopenharmony_ci        node = environment.parse(source)
6861cb0ef41Sopenharmony_ci        tokens = list(environment.lex(environment.preprocess(source)))
6871cb0ef41Sopenharmony_ci    except TemplateSyntaxError:
6881cb0ef41Sopenharmony_ci        if not silent:
6891cb0ef41Sopenharmony_ci            raise
6901cb0ef41Sopenharmony_ci        # skip templates with syntax errors
6911cb0ef41Sopenharmony_ci        return
6921cb0ef41Sopenharmony_ci
6931cb0ef41Sopenharmony_ci    finder = _CommentFinder(tokens, comment_tags)
6941cb0ef41Sopenharmony_ci    for lineno, func, message in extract_from_ast(node, keywords):
6951cb0ef41Sopenharmony_ci        yield lineno, func, message, finder.find_comments(lineno)
6961cb0ef41Sopenharmony_ci
6971cb0ef41Sopenharmony_ci
6981cb0ef41Sopenharmony_ci#: nicer import names
6991cb0ef41Sopenharmony_cii18n = InternationalizationExtension
7001cb0ef41Sopenharmony_cido = ExprStmtExtension
7011cb0ef41Sopenharmony_ciloopcontrols = LoopControlExtension
7021cb0ef41Sopenharmony_ciwith_ = WithExtension
7031cb0ef41Sopenharmony_ciautoescape = AutoEscapeExtension
7041cb0ef41Sopenharmony_cidebug = DebugExtension
705