Fixed a bug in the i18n extraction option handling and added a silent option.
[jinja2.git] / jinja2 / ext.py
index 48f04fc160ad2b999432da28cd9170414fe68a12..206756fe7921f13fcffdde556476d1b011aa2bd0 100644 (file)
@@ -13,7 +13,7 @@
 from collections import deque
 from jinja2 import nodes
 from jinja2.defaults import *
-from jinja2.environment import get_spontaneous_environment
+from jinja2.environment import Environment
 from jinja2.runtime import Undefined, concat
 from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
 from jinja2.utils import contextfunction, import_string, Markup, next
@@ -103,7 +103,9 @@ class Extension(object):
 
     def attr(self, name, lineno=None):
         """Return an attribute node for the current extension.  This is useful
-        to pass constants on extensions to generated template code::
+        to pass constants on extensions to generated template code.
+
+        ::
 
             self.attr('_my_attribute', lineno=lineno)
         """
@@ -139,9 +141,9 @@ def _make_new_gettext(func):
 
 def _make_new_ngettext(func):
     @contextfunction
-    def ngettext(__context, __singular, __plural, num, **variables):
-        variables.setdefault('num', num)
-        rv = __context.call(func, __singular, __plural, num)
+    def ngettext(__context, __singular, __plural, __num, **variables):
+        variables.setdefault('num', __num)
+        rv = __context.call(func, __singular, __plural, __num)
         if __context.eval_ctx.autoescape:
             rv = Markup(rv)
         return rv % variables
@@ -210,6 +212,7 @@ class InternationalizationExtension(Extension):
     def parse(self, parser):
         """Parse a translatable tag."""
         lineno = next(parser.stream).lineno
+        num_called_num = False
 
         # find all the variables referenced.  Additionally a variable can be
         # defined in the body of the trans block too, but this is checked at
@@ -236,8 +239,10 @@ class InternationalizationExtension(Extension):
                 variables[name.value] = var = parser.parse_expression()
             else:
                 variables[name.value] = var = nodes.Name(name.value, 'load')
+
             if plural_expr is None:
                 plural_expr = var
+                num_called_num = name.value == 'num'
 
         parser.stream.expect('block_end')
 
@@ -251,6 +256,7 @@ class InternationalizationExtension(Extension):
             referenced.update(singular_names)
             if plural_expr is None:
                 plural_expr = nodes.Name(singular_names[0], 'load')
+                num_called_num = singular_names[0] == 'num'
 
         # if we have a pluralize block, we parse that too
         if parser.stream.current.test('name:pluralize'):
@@ -263,6 +269,7 @@ class InternationalizationExtension(Extension):
                                 name.value, name.lineno,
                                 exc=TemplateAssertionError)
                 plural_expr = variables[name.value]
+                num_called_num = name.value == 'num'
             parser.stream.expect('block_end')
             plural_names, plural = self._parse_block(parser, False)
             next(parser.stream)
@@ -281,7 +288,8 @@ class InternationalizationExtension(Extension):
             parser.fail('pluralize without variables', lineno)
 
         node = self._make_node(singular, plural, variables, plural_expr,
-                               bool(referenced))
+                               bool(referenced),
+                               num_called_num and have_plural)
         node.set_lineno(lineno)
         return node
 
@@ -318,10 +326,10 @@ class InternationalizationExtension(Extension):
         return referenced, concat(buf)
 
     def _make_node(self, singular, plural, variables, plural_expr,
-                   vars_referenced):
+                   vars_referenced, num_called_num):
         """Generates a useful node from the data provided."""
         # no variables referenced?  no need to escape for old style
-        # gettext invocations
+        # gettext invocations only if there are vars.
         if not vars_referenced and not self.environment.newstyle_gettext:
             singular = singular.replace('%%', '%')
             if plural:
@@ -346,8 +354,11 @@ class InternationalizationExtension(Extension):
         # enough to handle the variable expansion and autoescape
         # handling itself
         if self.environment.newstyle_gettext:
-            print 'HERE'
             for key, value in variables.iteritems():
+                # the function adds that later anyways in case num was
+                # called num, so just skip it.
+                if num_called_num and key == 'num':
+                    continue
                 node.kwargs.append(nodes.Keyword(key, value))
 
         # otherwise do that here
@@ -537,6 +548,14 @@ def babel_extract(fileobj, keywords, comment_tags, options):
        gettext call in one line of code and the matching comment in the
        same line or the line before.
 
+    .. versionchanged:: 2.5.1
+       The `newstyle_gettext` flag can be set to `True` to enable newstyle
+       gettext calls.
+
+    .. versionchanged:: 2.7
+       A `silent` option can now be provided.  If set to `False` template
+       syntax errors are propagated instead of being ignored.
+
     :param fileobj: the file-like object the messages should be extracted from
     :param keywords: a list of keywords (i.e. function names) that should be
                      recognized as translation functions
@@ -555,7 +574,12 @@ def babel_extract(fileobj, keywords, comment_tags, options):
     if InternationalizationExtension not in extensions:
         extensions.add(InternationalizationExtension)
 
-    environment = get_spontaneous_environment(
+    def getbool(options, key, default=False):
+        return options.get(key, str(default)).lower() in \
+            ('1', 'on', 'yes', 'true')
+
+    silent = getbool(options, 'silent', True)
+    environment = Environment(
         options.get('block_start_string', BLOCK_START_STRING),
         options.get('block_end_string', BLOCK_END_STRING),
         options.get('variable_start_string', VARIABLE_START_STRING),
@@ -564,22 +588,22 @@ def babel_extract(fileobj, keywords, comment_tags, options):
         options.get('comment_end_string', COMMENT_END_STRING),
         options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX,
         options.get('line_comment_prefix') or LINE_COMMENT_PREFIX,
-        str(options.get('trim_blocks', TRIM_BLOCKS)).lower() in \
-            ('1', 'on', 'yes', 'true'),
+        getbool(options, 'trim_blocks', TRIM_BLOCKS),
         NEWLINE_SEQUENCE, frozenset(extensions),
-        # fill with defaults so that environments are shared
-        # with other spontaneus environments.  The rest of the
-        # arguments are optimizer, undefined, finalize, autoescape,
-        # loader, cache size, auto reloading setting and the
-        # bytecode cache
-        True, Undefined, None, False, None, 0, False, None
+        cache_size=0,
+        auto_reload=False
     )
 
+    if getbool(options, 'newstyle_gettext'):
+        environment.newstyle_gettext = True
+
     source = fileobj.read().decode(options.get('encoding', 'utf-8'))
     try:
         node = environment.parse(source)
         tokens = list(environment.lex(environment.preprocess(source)))
     except TemplateSyntaxError, e:
+        if not silent:
+            raise
         # skip templates with syntax errors
         return