Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: third_party/jinja2/ext.py

Issue 23506004: Update Jinja2 (Python template library) to 2.7.1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/jinja2/exceptions.py ('k') | third_party/jinja2/filters.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # -*- coding: utf-8 -*- 1 # -*- coding: utf-8 -*-
2 """ 2 """
3 jinja2.ext 3 jinja2.ext
4 ~~~~~~~~~~ 4 ~~~~~~~~~~
5 5
6 Jinja extensions allow to add custom tags similar to the way django custom 6 Jinja extensions allow to add custom tags similar to the way django custom
7 tags work. By default two example extensions exist: an i18n and a cache 7 tags work. By default two example extensions exist: an i18n and a cache
8 extension. 8 extension.
9 9
10 :copyright: (c) 2010 by the Jinja Team. 10 :copyright: (c) 2010 by the Jinja Team.
11 :license: BSD. 11 :license: BSD.
12 """ 12 """
13 from collections import deque
14 from jinja2 import nodes 13 from jinja2 import nodes
15 from jinja2.defaults import * 14 from jinja2.defaults import BLOCK_START_STRING, \
15 BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \
16 COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \
17 LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \
18 KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS
16 from jinja2.environment import Environment 19 from jinja2.environment import Environment
17 from jinja2.runtime import Undefined, concat 20 from jinja2.runtime import concat
18 from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError 21 from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
19 from jinja2.utils import contextfunction, import_string, Markup, next 22 from jinja2.utils import contextfunction, import_string, Markup
23 from jinja2._compat import next, with_metaclass, string_types, iteritems
20 24
21 25
22 # the only real useful gettext functions for a Jinja template. Note 26 # the only real useful gettext functions for a Jinja template. Note
23 # that ugettext must be assigned to gettext as Jinja doesn't support 27 # that ugettext must be assigned to gettext as Jinja doesn't support
24 # non unicode strings. 28 # non unicode strings.
25 GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext') 29 GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext')
26 30
27 31
28 class ExtensionRegistry(type): 32 class ExtensionRegistry(type):
29 """Gives the extension an unique identifier.""" 33 """Gives the extension an unique identifier."""
30 34
31 def __new__(cls, name, bases, d): 35 def __new__(cls, name, bases, d):
32 rv = type.__new__(cls, name, bases, d) 36 rv = type.__new__(cls, name, bases, d)
33 rv.identifier = rv.__module__ + '.' + rv.__name__ 37 rv.identifier = rv.__module__ + '.' + rv.__name__
34 return rv 38 return rv
35 39
36 40
37 class Extension(object): 41 class Extension(with_metaclass(ExtensionRegistry, object)):
38 """Extensions can be used to add extra functionality to the Jinja template 42 """Extensions can be used to add extra functionality to the Jinja template
39 system at the parser level. Custom extensions are bound to an environment 43 system at the parser level. Custom extensions are bound to an environment
40 but may not store environment specific data on `self`. The reason for 44 but may not store environment specific data on `self`. The reason for
41 this is that an extension can be bound to another environment (for 45 this is that an extension can be bound to another environment (for
42 overlays) by creating a copy and reassigning the `environment` attribute. 46 overlays) by creating a copy and reassigning the `environment` attribute.
43 47
44 As extensions are created by the environment they cannot accept any 48 As extensions are created by the environment they cannot accept any
45 arguments for configuration. One may want to work around that by using 49 arguments for configuration. One may want to work around that by using
46 a factory function, but that is not possible as extensions are identified 50 a factory function, but that is not possible as extensions are identified
47 by their import name. The correct way to configure the extension is 51 by their import name. The correct way to configure the extension is
48 storing the configuration values on the environment. Because this way the 52 storing the configuration values on the environment. Because this way the
49 environment ends up acting as central configuration storage the 53 environment ends up acting as central configuration storage the
50 attributes may clash which is why extensions have to ensure that the names 54 attributes may clash which is why extensions have to ensure that the names
51 they choose for configuration are not too generic. ``prefix`` for example 55 they choose for configuration are not too generic. ``prefix`` for example
52 is a terrible name, ``fragment_cache_prefix`` on the other hand is a good 56 is a terrible name, ``fragment_cache_prefix`` on the other hand is a good
53 name as includes the name of the extension (fragment cache). 57 name as includes the name of the extension (fragment cache).
54 """ 58 """
55 __metaclass__ = ExtensionRegistry
56 59
57 #: if this extension parses this is the list of tags it's listening to. 60 #: if this extension parses this is the list of tags it's listening to.
58 tags = set() 61 tags = set()
59 62
60 #: the priority of that extension. This is especially useful for 63 #: the priority of that extension. This is especially useful for
61 #: extensions that preprocess values. A lower value means higher 64 #: extensions that preprocess values. A lower value means higher
62 #: priority. 65 #: priority.
63 #: 66 #:
64 #: .. versionadded:: 2.4 67 #: .. versionadded:: 2.4
65 priority = 100 68 priority = 100
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 self.environment.globals.update( 201 self.environment.globals.update(
199 gettext=gettext, 202 gettext=gettext,
200 ngettext=ngettext 203 ngettext=ngettext
201 ) 204 )
202 205
203 def _uninstall(self, translations): 206 def _uninstall(self, translations):
204 for key in 'gettext', 'ngettext': 207 for key in 'gettext', 'ngettext':
205 self.environment.globals.pop(key, None) 208 self.environment.globals.pop(key, None)
206 209
207 def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS): 210 def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
208 if isinstance(source, basestring): 211 if isinstance(source, string_types):
209 source = self.environment.parse(source) 212 source = self.environment.parse(source)
210 return extract_from_ast(source, gettext_functions) 213 return extract_from_ast(source, gettext_functions)
211 214
212 def parse(self, parser): 215 def parse(self, parser):
213 """Parse a translatable tag.""" 216 """Parse a translatable tag."""
214 lineno = next(parser.stream).lineno 217 lineno = next(parser.stream).lineno
215 num_called_num = False 218 num_called_num = False
216 219
217 # find all the variables referenced. Additionally a variable can be 220 # find all the variables referenced. Additionally a variable can be
218 # defined in the body of the trans block too, but this is checked at 221 # defined in the body of the trans block too, but this is checked at
219 # a later state. 222 # a later state.
220 plural_expr = None 223 plural_expr = None
224 plural_expr_assignment = None
221 variables = {} 225 variables = {}
222 while parser.stream.current.type != 'block_end': 226 while parser.stream.current.type != 'block_end':
223 if variables: 227 if variables:
224 parser.stream.expect('comma') 228 parser.stream.expect('comma')
225 229
226 # skip colon for python compatibility 230 # skip colon for python compatibility
227 if parser.stream.skip_if('colon'): 231 if parser.stream.skip_if('colon'):
228 break 232 break
229 233
230 name = parser.stream.expect('name') 234 name = parser.stream.expect('name')
231 if name.value in variables: 235 if name.value in variables:
232 parser.fail('translatable variable %r defined twice.' % 236 parser.fail('translatable variable %r defined twice.' %
233 name.value, name.lineno, 237 name.value, name.lineno,
234 exc=TemplateAssertionError) 238 exc=TemplateAssertionError)
235 239
236 # expressions 240 # expressions
237 if parser.stream.current.type == 'assign': 241 if parser.stream.current.type == 'assign':
238 next(parser.stream) 242 next(parser.stream)
239 variables[name.value] = var = parser.parse_expression() 243 variables[name.value] = var = parser.parse_expression()
240 else: 244 else:
241 variables[name.value] = var = nodes.Name(name.value, 'load') 245 variables[name.value] = var = nodes.Name(name.value, 'load')
242 246
243 if plural_expr is None: 247 if plural_expr is None:
244 plural_expr = var 248 if isinstance(var, nodes.Call):
249 plural_expr = nodes.Name('_trans', 'load')
250 variables[name.value] = plural_expr
251 plural_expr_assignment = nodes.Assign(
252 nodes.Name('_trans', 'store'), var)
253 else:
254 plural_expr = var
245 num_called_num = name.value == 'num' 255 num_called_num = name.value == 'num'
246 256
247 parser.stream.expect('block_end') 257 parser.stream.expect('block_end')
248 258
249 plural = plural_names = None 259 plural = plural_names = None
250 have_plural = False 260 have_plural = False
251 referenced = set() 261 referenced = set()
252 262
253 # now parse until endtrans or pluralize 263 # now parse until endtrans or pluralize
254 singular_names, singular = self._parse_block(parser, True) 264 singular_names, singular = self._parse_block(parser, True)
(...skipping 29 matching lines...) Expand all
284 294
285 if not have_plural: 295 if not have_plural:
286 plural_expr = None 296 plural_expr = None
287 elif plural_expr is None: 297 elif plural_expr is None:
288 parser.fail('pluralize without variables', lineno) 298 parser.fail('pluralize without variables', lineno)
289 299
290 node = self._make_node(singular, plural, variables, plural_expr, 300 node = self._make_node(singular, plural, variables, plural_expr,
291 bool(referenced), 301 bool(referenced),
292 num_called_num and have_plural) 302 num_called_num and have_plural)
293 node.set_lineno(lineno) 303 node.set_lineno(lineno)
294 return node 304 if plural_expr_assignment is not None:
305 return [plural_expr_assignment, node]
306 else:
307 return node
295 308
296 def _parse_block(self, parser, allow_pluralize): 309 def _parse_block(self, parser, allow_pluralize):
297 """Parse until the next block tag with a given name.""" 310 """Parse until the next block tag with a given name."""
298 referenced = [] 311 referenced = []
299 buf = [] 312 buf = []
300 while 1: 313 while 1:
301 if parser.stream.current.type == 'data': 314 if parser.stream.current.type == 'data':
302 buf.append(parser.stream.current.value.replace('%', '%%')) 315 buf.append(parser.stream.current.value.replace('%', '%%'))
303 next(parser.stream) 316 next(parser.stream)
304 elif parser.stream.current.type == 'variable_begin': 317 elif parser.stream.current.type == 'variable_begin':
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 node = nodes.Call(ngettext, [ 360 node = nodes.Call(ngettext, [
348 nodes.Const(singular), 361 nodes.Const(singular),
349 nodes.Const(plural), 362 nodes.Const(plural),
350 plural_expr 363 plural_expr
351 ], [], None, None) 364 ], [], None, None)
352 365
353 # in case newstyle gettext is used, the method is powerful 366 # in case newstyle gettext is used, the method is powerful
354 # enough to handle the variable expansion and autoescape 367 # enough to handle the variable expansion and autoescape
355 # handling itself 368 # handling itself
356 if self.environment.newstyle_gettext: 369 if self.environment.newstyle_gettext:
357 for key, value in variables.iteritems(): 370 for key, value in iteritems(variables):
358 # the function adds that later anyways in case num was 371 # the function adds that later anyways in case num was
359 # called num, so just skip it. 372 # called num, so just skip it.
360 if num_called_num and key == 'num': 373 if num_called_num and key == 'num':
361 continue 374 continue
362 node.kwargs.append(nodes.Keyword(key, value)) 375 node.kwargs.append(nodes.Keyword(key, value))
363 376
364 # otherwise do that here 377 # otherwise do that here
365 else: 378 else:
366 # mark the return value as safe if we are in an 379 # mark the return value as safe if we are in an
367 # environment with autoescaping turned on 380 # environment with autoescaping turned on
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 extraction interface or extract comments yourself. 482 extraction interface or extract comments yourself.
470 """ 483 """
471 for node in node.find_all(nodes.Call): 484 for node in node.find_all(nodes.Call):
472 if not isinstance(node.node, nodes.Name) or \ 485 if not isinstance(node.node, nodes.Name) or \
473 node.node.name not in gettext_functions: 486 node.node.name not in gettext_functions:
474 continue 487 continue
475 488
476 strings = [] 489 strings = []
477 for arg in node.args: 490 for arg in node.args:
478 if isinstance(arg, nodes.Const) and \ 491 if isinstance(arg, nodes.Const) and \
479 isinstance(arg.value, basestring): 492 isinstance(arg.value, string_types):
480 strings.append(arg.value) 493 strings.append(arg.value)
481 else: 494 else:
482 strings.append(None) 495 strings.append(None)
483 496
484 for arg in node.kwargs: 497 for arg in node.kwargs:
485 strings.append(None) 498 strings.append(None)
486 if node.dyn_args is not None: 499 if node.dyn_args is not None:
487 strings.append(None) 500 strings.append(None)
488 if node.dyn_kwargs is not None: 501 if node.dyn_kwargs is not None:
489 strings.append(None) 502 strings.append(None)
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
545 is now set to a list of keywords for extraction, the extractor will 558 is now set to a list of keywords for extraction, the extractor will
546 try to find the best preceeding comment that begins with one of the 559 try to find the best preceeding comment that begins with one of the
547 keywords. For best results, make sure to not have more than one 560 keywords. For best results, make sure to not have more than one
548 gettext call in one line of code and the matching comment in the 561 gettext call in one line of code and the matching comment in the
549 same line or the line before. 562 same line or the line before.
550 563
551 .. versionchanged:: 2.5.1 564 .. versionchanged:: 2.5.1
552 The `newstyle_gettext` flag can be set to `True` to enable newstyle 565 The `newstyle_gettext` flag can be set to `True` to enable newstyle
553 gettext calls. 566 gettext calls.
554 567
568 .. versionchanged:: 2.7
569 A `silent` option can now be provided. If set to `False` template
570 syntax errors are propagated instead of being ignored.
571
555 :param fileobj: the file-like object the messages should be extracted from 572 :param fileobj: the file-like object the messages should be extracted from
556 :param keywords: a list of keywords (i.e. function names) that should be 573 :param keywords: a list of keywords (i.e. function names) that should be
557 recognized as translation functions 574 recognized as translation functions
558 :param comment_tags: a list of translator tags to search for and include 575 :param comment_tags: a list of translator tags to search for and include
559 in the results. 576 in the results.
560 :param options: a dictionary of additional options (optional) 577 :param options: a dictionary of additional options (optional)
561 :return: an iterator over ``(lineno, funcname, message, comments)`` tuples. 578 :return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
562 (comments will be empty currently) 579 (comments will be empty currently)
563 """ 580 """
564 extensions = set() 581 extensions = set()
565 for extension in options.get('extensions', '').split(','): 582 for extension in options.get('extensions', '').split(','):
566 extension = extension.strip() 583 extension = extension.strip()
567 if not extension: 584 if not extension:
568 continue 585 continue
569 extensions.add(import_string(extension)) 586 extensions.add(import_string(extension))
570 if InternationalizationExtension not in extensions: 587 if InternationalizationExtension not in extensions:
571 extensions.add(InternationalizationExtension) 588 extensions.add(InternationalizationExtension)
572 589
573 def getbool(options, key, default=False): 590 def getbool(options, key, default=False):
574 options.get(key, str(default)).lower() in ('1', 'on', 'yes', 'true') 591 return options.get(key, str(default)).lower() in \
592 ('1', 'on', 'yes', 'true')
575 593
594 silent = getbool(options, 'silent', True)
576 environment = Environment( 595 environment = Environment(
577 options.get('block_start_string', BLOCK_START_STRING), 596 options.get('block_start_string', BLOCK_START_STRING),
578 options.get('block_end_string', BLOCK_END_STRING), 597 options.get('block_end_string', BLOCK_END_STRING),
579 options.get('variable_start_string', VARIABLE_START_STRING), 598 options.get('variable_start_string', VARIABLE_START_STRING),
580 options.get('variable_end_string', VARIABLE_END_STRING), 599 options.get('variable_end_string', VARIABLE_END_STRING),
581 options.get('comment_start_string', COMMENT_START_STRING), 600 options.get('comment_start_string', COMMENT_START_STRING),
582 options.get('comment_end_string', COMMENT_END_STRING), 601 options.get('comment_end_string', COMMENT_END_STRING),
583 options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX, 602 options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX,
584 options.get('line_comment_prefix') or LINE_COMMENT_PREFIX, 603 options.get('line_comment_prefix') or LINE_COMMENT_PREFIX,
585 getbool(options, 'trim_blocks', TRIM_BLOCKS), 604 getbool(options, 'trim_blocks', TRIM_BLOCKS),
586 NEWLINE_SEQUENCE, frozenset(extensions), 605 getbool(options, 'lstrip_blocks', LSTRIP_BLOCKS),
606 NEWLINE_SEQUENCE,
607 getbool(options, 'keep_trailing_newline', KEEP_TRAILING_NEWLINE),
608 frozenset(extensions),
587 cache_size=0, 609 cache_size=0,
588 auto_reload=False 610 auto_reload=False
589 ) 611 )
590 612
591 if getbool(options, 'newstyle_gettext'): 613 if getbool(options, 'newstyle_gettext'):
592 environment.newstyle_gettext = True 614 environment.newstyle_gettext = True
593 615
594 source = fileobj.read().decode(options.get('encoding', 'utf-8')) 616 source = fileobj.read().decode(options.get('encoding', 'utf-8'))
595 try: 617 try:
596 node = environment.parse(source) 618 node = environment.parse(source)
597 tokens = list(environment.lex(environment.preprocess(source))) 619 tokens = list(environment.lex(environment.preprocess(source)))
598 except TemplateSyntaxError, e: 620 except TemplateSyntaxError as e:
621 if not silent:
622 raise
599 # skip templates with syntax errors 623 # skip templates with syntax errors
600 return 624 return
601 625
602 finder = _CommentFinder(tokens, comment_tags) 626 finder = _CommentFinder(tokens, comment_tags)
603 for lineno, func, message in extract_from_ast(node, keywords): 627 for lineno, func, message in extract_from_ast(node, keywords):
604 yield lineno, func, message, finder.find_comments(lineno) 628 yield lineno, func, message, finder.find_comments(lineno)
605 629
606 630
607 #: nicer import names 631 #: nicer import names
608 i18n = InternationalizationExtension 632 i18n = InternationalizationExtension
609 do = ExprStmtExtension 633 do = ExprStmtExtension
610 loopcontrols = LoopControlExtension 634 loopcontrols = LoopControlExtension
611 with_ = WithExtension 635 with_ = WithExtension
612 autoescape = AutoEscapeExtension 636 autoescape = AutoEscapeExtension
OLDNEW
« no previous file with comments | « third_party/jinja2/exceptions.py ('k') | third_party/jinja2/filters.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698