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