jinja2.parser
Parse tokens from the lexer into nodes for the compiler.
1"""Parse tokens from the lexer into nodes for the compiler.""" 2 3import typing 4import typing as t 5 6from . import nodes 7from .exceptions import TemplateAssertionError 8from .exceptions import TemplateSyntaxError 9from .lexer import describe_token 10from .lexer import describe_token_expr 11 12if t.TYPE_CHECKING: 13 import typing_extensions as te 14 15 from .environment import Environment 16 17_ImportInclude = t.TypeVar("_ImportInclude", nodes.Import, nodes.Include) 18_MacroCall = t.TypeVar("_MacroCall", nodes.Macro, nodes.CallBlock) 19 20_statement_keywords = frozenset( 21 [ 22 "for", 23 "if", 24 "block", 25 "extends", 26 "print", 27 "macro", 28 "include", 29 "from", 30 "import", 31 "set", 32 "with", 33 "autoescape", 34 ] 35) 36_compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"]) 37 38_math_nodes: t.Dict[str, t.Type[nodes.Expr]] = { 39 "add": nodes.Add, 40 "sub": nodes.Sub, 41 "mul": nodes.Mul, 42 "div": nodes.Div, 43 "floordiv": nodes.FloorDiv, 44 "mod": nodes.Mod, 45} 46 47 48class Parser: 49 """This is the central parsing class Jinja uses. It's passed to 50 extensions and can be used to parse expressions or statements. 51 """ 52 53 def __init__( 54 self, 55 environment: "Environment", 56 source: str, 57 name: t.Optional[str] = None, 58 filename: t.Optional[str] = None, 59 state: t.Optional[str] = None, 60 ) -> None: 61 self.environment = environment 62 self.stream = environment._tokenize(source, name, filename, state) 63 self.name = name 64 self.filename = filename 65 self.closed = False 66 self.extensions: t.Dict[ 67 str, t.Callable[["Parser"], t.Union[nodes.Node, t.List[nodes.Node]]] 68 ] = {} 69 for extension in environment.iter_extensions(): 70 for tag in extension.tags: 71 self.extensions[tag] = extension.parse 72 self._last_identifier = 0 73 self._tag_stack: t.List[str] = [] 74 self._end_token_stack: t.List[t.Tuple[str, ...]] = [] 75 76 def fail( 77 self, 78 msg: str, 79 lineno: t.Optional[int] = None, 80 exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError, 81 ) -> "te.NoReturn": 82 """Convenience method that raises `exc` with the message, passed 83 line number or last line number as well as the current name and 84 filename. 85 """ 86 if lineno is None: 87 lineno = self.stream.current.lineno 88 raise exc(msg, lineno, self.name, self.filename) 89 90 def _fail_ut_eof( 91 self, 92 name: t.Optional[str], 93 end_token_stack: t.List[t.Tuple[str, ...]], 94 lineno: t.Optional[int], 95 ) -> "te.NoReturn": 96 expected: t.Set[str] = set() 97 for exprs in end_token_stack: 98 expected.update(map(describe_token_expr, exprs)) 99 if end_token_stack: 100 currently_looking: t.Optional[str] = " or ".join( 101 map(repr, map(describe_token_expr, end_token_stack[-1])) 102 ) 103 else: 104 currently_looking = None 105 106 if name is None: 107 message = ["Unexpected end of template."] 108 else: 109 message = [f"Encountered unknown tag {name!r}."] 110 111 if currently_looking: 112 if name is not None and name in expected: 113 message.append( 114 "You probably made a nesting mistake. Jinja is expecting this tag," 115 f" but currently looking for {currently_looking}." 116 ) 117 else: 118 message.append( 119 f"Jinja was looking for the following tags: {currently_looking}." 120 ) 121 122 if self._tag_stack: 123 message.append( 124 "The innermost block that needs to be closed is" 125 f" {self._tag_stack[-1]!r}." 126 ) 127 128 self.fail(" ".join(message), lineno) 129 130 def fail_unknown_tag( 131 self, name: str, lineno: t.Optional[int] = None 132 ) -> "te.NoReturn": 133 """Called if the parser encounters an unknown tag. Tries to fail 134 with a human readable error message that could help to identify 135 the problem. 136 """ 137 self._fail_ut_eof(name, self._end_token_stack, lineno) 138 139 def fail_eof( 140 self, 141 end_tokens: t.Optional[t.Tuple[str, ...]] = None, 142 lineno: t.Optional[int] = None, 143 ) -> "te.NoReturn": 144 """Like fail_unknown_tag but for end of template situations.""" 145 stack = list(self._end_token_stack) 146 if end_tokens is not None: 147 stack.append(end_tokens) 148 self._fail_ut_eof(None, stack, lineno) 149 150 def is_tuple_end( 151 self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None 152 ) -> bool: 153 """Are we at the end of a tuple?""" 154 if self.stream.current.type in ("variable_end", "block_end", "rparen"): 155 return True 156 elif extra_end_rules is not None: 157 return self.stream.current.test_any(extra_end_rules) # type: ignore 158 return False 159 160 def free_identifier(self, lineno: t.Optional[int] = None) -> nodes.InternalName: 161 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" 162 self._last_identifier += 1 163 rv = object.__new__(nodes.InternalName) 164 nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno) 165 return rv 166 167 def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]: 168 """Parse a single statement.""" 169 token = self.stream.current 170 if token.type != "name": 171 self.fail("tag name expected", token.lineno) 172 self._tag_stack.append(token.value) 173 pop_tag = True 174 try: 175 if token.value in _statement_keywords: 176 f = getattr(self, f"parse_{self.stream.current.value}") 177 return f() # type: ignore 178 if token.value == "call": 179 return self.parse_call_block() 180 if token.value == "filter": 181 return self.parse_filter_block() 182 ext = self.extensions.get(token.value) 183 if ext is not None: 184 return ext(self) 185 186 # did not work out, remove the token we pushed by accident 187 # from the stack so that the unknown tag fail function can 188 # produce a proper error message. 189 self._tag_stack.pop() 190 pop_tag = False 191 self.fail_unknown_tag(token.value, token.lineno) 192 finally: 193 if pop_tag: 194 self._tag_stack.pop() 195 196 def parse_statements( 197 self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False 198 ) -> t.List[nodes.Node]: 199 """Parse multiple statements into a list until one of the end tokens 200 is reached. This is used to parse the body of statements as it also 201 parses template data if appropriate. The parser checks first if the 202 current token is a colon and skips it if there is one. Then it checks 203 for the block end and parses until if one of the `end_tokens` is 204 reached. Per default the active token in the stream at the end of 205 the call is the matched end token. If this is not wanted `drop_needle` 206 can be set to `True` and the end token is removed. 207 """ 208 # the first token may be a colon for python compatibility 209 self.stream.skip_if("colon") 210 211 # in the future it would be possible to add whole code sections 212 # by adding some sort of end of statement token and parsing those here. 213 self.stream.expect("block_end") 214 result = self.subparse(end_tokens) 215 216 # we reached the end of the template too early, the subparser 217 # does not check for this, so we do that now 218 if self.stream.current.type == "eof": 219 self.fail_eof(end_tokens) 220 221 if drop_needle: 222 next(self.stream) 223 return result 224 225 def parse_set(self) -> t.Union[nodes.Assign, nodes.AssignBlock]: 226 """Parse an assign statement.""" 227 lineno = next(self.stream).lineno 228 target = self.parse_assign_target(with_namespace=True) 229 if self.stream.skip_if("assign"): 230 expr = self.parse_tuple() 231 return nodes.Assign(target, expr, lineno=lineno) 232 filter_node = self.parse_filter(None) 233 body = self.parse_statements(("name:endset",), drop_needle=True) 234 return nodes.AssignBlock(target, filter_node, body, lineno=lineno) 235 236 def parse_for(self) -> nodes.For: 237 """Parse a for loop.""" 238 lineno = self.stream.expect("name:for").lineno 239 target = self.parse_assign_target(extra_end_rules=("name:in",)) 240 self.stream.expect("name:in") 241 iter = self.parse_tuple( 242 with_condexpr=False, extra_end_rules=("name:recursive",) 243 ) 244 test = None 245 if self.stream.skip_if("name:if"): 246 test = self.parse_expression() 247 recursive = self.stream.skip_if("name:recursive") 248 body = self.parse_statements(("name:endfor", "name:else")) 249 if next(self.stream).value == "endfor": 250 else_ = [] 251 else: 252 else_ = self.parse_statements(("name:endfor",), drop_needle=True) 253 return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno) 254 255 def parse_if(self) -> nodes.If: 256 """Parse an if construct.""" 257 node = result = nodes.If(lineno=self.stream.expect("name:if").lineno) 258 while True: 259 node.test = self.parse_tuple(with_condexpr=False) 260 node.body = self.parse_statements(("name:elif", "name:else", "name:endif")) 261 node.elif_ = [] 262 node.else_ = [] 263 token = next(self.stream) 264 if token.test("name:elif"): 265 node = nodes.If(lineno=self.stream.current.lineno) 266 result.elif_.append(node) 267 continue 268 elif token.test("name:else"): 269 result.else_ = self.parse_statements(("name:endif",), drop_needle=True) 270 break 271 return result 272 273 def parse_with(self) -> nodes.With: 274 node = nodes.With(lineno=next(self.stream).lineno) 275 targets: t.List[nodes.Expr] = [] 276 values: t.List[nodes.Expr] = [] 277 while self.stream.current.type != "block_end": 278 if targets: 279 self.stream.expect("comma") 280 target = self.parse_assign_target() 281 target.set_ctx("param") 282 targets.append(target) 283 self.stream.expect("assign") 284 values.append(self.parse_expression()) 285 node.targets = targets 286 node.values = values 287 node.body = self.parse_statements(("name:endwith",), drop_needle=True) 288 return node 289 290 def parse_autoescape(self) -> nodes.Scope: 291 node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno) 292 node.options = [nodes.Keyword("autoescape", self.parse_expression())] 293 node.body = self.parse_statements(("name:endautoescape",), drop_needle=True) 294 return nodes.Scope([node]) 295 296 def parse_block(self) -> nodes.Block: 297 node = nodes.Block(lineno=next(self.stream).lineno) 298 node.name = self.stream.expect("name").value 299 node.scoped = self.stream.skip_if("name:scoped") 300 node.required = self.stream.skip_if("name:required") 301 302 # common problem people encounter when switching from django 303 # to jinja. we do not support hyphens in block names, so let's 304 # raise a nicer error message in that case. 305 if self.stream.current.type == "sub": 306 self.fail( 307 "Block names in Jinja have to be valid Python identifiers and may not" 308 " contain hyphens, use an underscore instead." 309 ) 310 311 node.body = self.parse_statements(("name:endblock",), drop_needle=True) 312 313 # enforce that required blocks only contain whitespace or comments 314 # by asserting that the body, if not empty, is just TemplateData nodes 315 # with whitespace data 316 if node.required: 317 for body_node in node.body: 318 if not isinstance(body_node, nodes.Output) or any( 319 not isinstance(output_node, nodes.TemplateData) 320 or not output_node.data.isspace() 321 for output_node in body_node.nodes 322 ): 323 self.fail("Required blocks can only contain comments or whitespace") 324 325 self.stream.skip_if("name:" + node.name) 326 return node 327 328 def parse_extends(self) -> nodes.Extends: 329 node = nodes.Extends(lineno=next(self.stream).lineno) 330 node.template = self.parse_expression() 331 return node 332 333 def parse_import_context( 334 self, node: _ImportInclude, default: bool 335 ) -> _ImportInclude: 336 if self.stream.current.test_any( 337 "name:with", "name:without" 338 ) and self.stream.look().test("name:context"): 339 node.with_context = next(self.stream).value == "with" 340 self.stream.skip() 341 else: 342 node.with_context = default 343 return node 344 345 def parse_include(self) -> nodes.Include: 346 node = nodes.Include(lineno=next(self.stream).lineno) 347 node.template = self.parse_expression() 348 if self.stream.current.test("name:ignore") and self.stream.look().test( 349 "name:missing" 350 ): 351 node.ignore_missing = True 352 self.stream.skip(2) 353 else: 354 node.ignore_missing = False 355 return self.parse_import_context(node, True) 356 357 def parse_import(self) -> nodes.Import: 358 node = nodes.Import(lineno=next(self.stream).lineno) 359 node.template = self.parse_expression() 360 self.stream.expect("name:as") 361 node.target = self.parse_assign_target(name_only=True).name 362 return self.parse_import_context(node, False) 363 364 def parse_from(self) -> nodes.FromImport: 365 node = nodes.FromImport(lineno=next(self.stream).lineno) 366 node.template = self.parse_expression() 367 self.stream.expect("name:import") 368 node.names = [] 369 370 def parse_context() -> bool: 371 if self.stream.current.value in { 372 "with", 373 "without", 374 } and self.stream.look().test("name:context"): 375 node.with_context = next(self.stream).value == "with" 376 self.stream.skip() 377 return True 378 return False 379 380 while True: 381 if node.names: 382 self.stream.expect("comma") 383 if self.stream.current.type == "name": 384 if parse_context(): 385 break 386 target = self.parse_assign_target(name_only=True) 387 if target.name.startswith("_"): 388 self.fail( 389 "names starting with an underline can not be imported", 390 target.lineno, 391 exc=TemplateAssertionError, 392 ) 393 if self.stream.skip_if("name:as"): 394 alias = self.parse_assign_target(name_only=True) 395 node.names.append((target.name, alias.name)) 396 else: 397 node.names.append(target.name) 398 if parse_context() or self.stream.current.type != "comma": 399 break 400 else: 401 self.stream.expect("name") 402 if not hasattr(node, "with_context"): 403 node.with_context = False 404 return node 405 406 def parse_signature(self, node: _MacroCall) -> None: 407 args = node.args = [] 408 defaults = node.defaults = [] 409 self.stream.expect("lparen") 410 while self.stream.current.type != "rparen": 411 if args: 412 self.stream.expect("comma") 413 arg = self.parse_assign_target(name_only=True) 414 arg.set_ctx("param") 415 if self.stream.skip_if("assign"): 416 defaults.append(self.parse_expression()) 417 elif defaults: 418 self.fail("non-default argument follows default argument") 419 args.append(arg) 420 self.stream.expect("rparen") 421 422 def parse_call_block(self) -> nodes.CallBlock: 423 node = nodes.CallBlock(lineno=next(self.stream).lineno) 424 if self.stream.current.type == "lparen": 425 self.parse_signature(node) 426 else: 427 node.args = [] 428 node.defaults = [] 429 430 call_node = self.parse_expression() 431 if not isinstance(call_node, nodes.Call): 432 self.fail("expected call", node.lineno) 433 node.call = call_node 434 node.body = self.parse_statements(("name:endcall",), drop_needle=True) 435 return node 436 437 def parse_filter_block(self) -> nodes.FilterBlock: 438 node = nodes.FilterBlock(lineno=next(self.stream).lineno) 439 node.filter = self.parse_filter(None, start_inline=True) # type: ignore 440 node.body = self.parse_statements(("name:endfilter",), drop_needle=True) 441 return node 442 443 def parse_macro(self) -> nodes.Macro: 444 node = nodes.Macro(lineno=next(self.stream).lineno) 445 node.name = self.parse_assign_target(name_only=True).name 446 self.parse_signature(node) 447 node.body = self.parse_statements(("name:endmacro",), drop_needle=True) 448 return node 449 450 def parse_print(self) -> nodes.Output: 451 node = nodes.Output(lineno=next(self.stream).lineno) 452 node.nodes = [] 453 while self.stream.current.type != "block_end": 454 if node.nodes: 455 self.stream.expect("comma") 456 node.nodes.append(self.parse_expression()) 457 return node 458 459 @typing.overload 460 def parse_assign_target( 461 self, with_tuple: bool = ..., name_only: "te.Literal[True]" = ... 462 ) -> nodes.Name: ... 463 464 @typing.overload 465 def parse_assign_target( 466 self, 467 with_tuple: bool = True, 468 name_only: bool = False, 469 extra_end_rules: t.Optional[t.Tuple[str, ...]] = None, 470 with_namespace: bool = False, 471 ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]: ... 472 473 def parse_assign_target( 474 self, 475 with_tuple: bool = True, 476 name_only: bool = False, 477 extra_end_rules: t.Optional[t.Tuple[str, ...]] = None, 478 with_namespace: bool = False, 479 ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]: 480 """Parse an assignment target. As Jinja allows assignments to 481 tuples, this function can parse all allowed assignment targets. Per 482 default assignments to tuples are parsed, that can be disable however 483 by setting `with_tuple` to `False`. If only assignments to names are 484 wanted `name_only` can be set to `True`. The `extra_end_rules` 485 parameter is forwarded to the tuple parsing function. If 486 `with_namespace` is enabled, a namespace assignment may be parsed. 487 """ 488 target: nodes.Expr 489 490 if with_namespace and self.stream.look().type == "dot": 491 token = self.stream.expect("name") 492 next(self.stream) # dot 493 attr = self.stream.expect("name") 494 target = nodes.NSRef(token.value, attr.value, lineno=token.lineno) 495 elif name_only: 496 token = self.stream.expect("name") 497 target = nodes.Name(token.value, "store", lineno=token.lineno) 498 else: 499 if with_tuple: 500 target = self.parse_tuple( 501 simplified=True, extra_end_rules=extra_end_rules 502 ) 503 else: 504 target = self.parse_primary() 505 506 target.set_ctx("store") 507 508 if not target.can_assign(): 509 self.fail( 510 f"can't assign to {type(target).__name__.lower()!r}", target.lineno 511 ) 512 513 return target # type: ignore 514 515 def parse_expression(self, with_condexpr: bool = True) -> nodes.Expr: 516 """Parse an expression. Per default all expressions are parsed, if 517 the optional `with_condexpr` parameter is set to `False` conditional 518 expressions are not parsed. 519 """ 520 if with_condexpr: 521 return self.parse_condexpr() 522 return self.parse_or() 523 524 def parse_condexpr(self) -> nodes.Expr: 525 lineno = self.stream.current.lineno 526 expr1 = self.parse_or() 527 expr3: t.Optional[nodes.Expr] 528 529 while self.stream.skip_if("name:if"): 530 expr2 = self.parse_or() 531 if self.stream.skip_if("name:else"): 532 expr3 = self.parse_condexpr() 533 else: 534 expr3 = None 535 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno) 536 lineno = self.stream.current.lineno 537 return expr1 538 539 def parse_or(self) -> nodes.Expr: 540 lineno = self.stream.current.lineno 541 left = self.parse_and() 542 while self.stream.skip_if("name:or"): 543 right = self.parse_and() 544 left = nodes.Or(left, right, lineno=lineno) 545 lineno = self.stream.current.lineno 546 return left 547 548 def parse_and(self) -> nodes.Expr: 549 lineno = self.stream.current.lineno 550 left = self.parse_not() 551 while self.stream.skip_if("name:and"): 552 right = self.parse_not() 553 left = nodes.And(left, right, lineno=lineno) 554 lineno = self.stream.current.lineno 555 return left 556 557 def parse_not(self) -> nodes.Expr: 558 if self.stream.current.test("name:not"): 559 lineno = next(self.stream).lineno 560 return nodes.Not(self.parse_not(), lineno=lineno) 561 return self.parse_compare() 562 563 def parse_compare(self) -> nodes.Expr: 564 lineno = self.stream.current.lineno 565 expr = self.parse_math1() 566 ops = [] 567 while True: 568 token_type = self.stream.current.type 569 if token_type in _compare_operators: 570 next(self.stream) 571 ops.append(nodes.Operand(token_type, self.parse_math1())) 572 elif self.stream.skip_if("name:in"): 573 ops.append(nodes.Operand("in", self.parse_math1())) 574 elif self.stream.current.test("name:not") and self.stream.look().test( 575 "name:in" 576 ): 577 self.stream.skip(2) 578 ops.append(nodes.Operand("notin", self.parse_math1())) 579 else: 580 break 581 lineno = self.stream.current.lineno 582 if not ops: 583 return expr 584 return nodes.Compare(expr, ops, lineno=lineno) 585 586 def parse_math1(self) -> nodes.Expr: 587 lineno = self.stream.current.lineno 588 left = self.parse_concat() 589 while self.stream.current.type in ("add", "sub"): 590 cls = _math_nodes[self.stream.current.type] 591 next(self.stream) 592 right = self.parse_concat() 593 left = cls(left, right, lineno=lineno) 594 lineno = self.stream.current.lineno 595 return left 596 597 def parse_concat(self) -> nodes.Expr: 598 lineno = self.stream.current.lineno 599 args = [self.parse_math2()] 600 while self.stream.current.type == "tilde": 601 next(self.stream) 602 args.append(self.parse_math2()) 603 if len(args) == 1: 604 return args[0] 605 return nodes.Concat(args, lineno=lineno) 606 607 def parse_math2(self) -> nodes.Expr: 608 lineno = self.stream.current.lineno 609 left = self.parse_pow() 610 while self.stream.current.type in ("mul", "div", "floordiv", "mod"): 611 cls = _math_nodes[self.stream.current.type] 612 next(self.stream) 613 right = self.parse_pow() 614 left = cls(left, right, lineno=lineno) 615 lineno = self.stream.current.lineno 616 return left 617 618 def parse_pow(self) -> nodes.Expr: 619 lineno = self.stream.current.lineno 620 left = self.parse_unary() 621 while self.stream.current.type == "pow": 622 next(self.stream) 623 right = self.parse_unary() 624 left = nodes.Pow(left, right, lineno=lineno) 625 lineno = self.stream.current.lineno 626 return left 627 628 def parse_unary(self, with_filter: bool = True) -> nodes.Expr: 629 token_type = self.stream.current.type 630 lineno = self.stream.current.lineno 631 node: nodes.Expr 632 633 if token_type == "sub": 634 next(self.stream) 635 node = nodes.Neg(self.parse_unary(False), lineno=lineno) 636 elif token_type == "add": 637 next(self.stream) 638 node = nodes.Pos(self.parse_unary(False), lineno=lineno) 639 else: 640 node = self.parse_primary() 641 node = self.parse_postfix(node) 642 if with_filter: 643 node = self.parse_filter_expr(node) 644 return node 645 646 def parse_primary(self) -> nodes.Expr: 647 token = self.stream.current 648 node: nodes.Expr 649 if token.type == "name": 650 if token.value in ("true", "false", "True", "False"): 651 node = nodes.Const(token.value in ("true", "True"), lineno=token.lineno) 652 elif token.value in ("none", "None"): 653 node = nodes.Const(None, lineno=token.lineno) 654 else: 655 node = nodes.Name(token.value, "load", lineno=token.lineno) 656 next(self.stream) 657 elif token.type == "string": 658 next(self.stream) 659 buf = [token.value] 660 lineno = token.lineno 661 while self.stream.current.type == "string": 662 buf.append(self.stream.current.value) 663 next(self.stream) 664 node = nodes.Const("".join(buf), lineno=lineno) 665 elif token.type in ("integer", "float"): 666 next(self.stream) 667 node = nodes.Const(token.value, lineno=token.lineno) 668 elif token.type == "lparen": 669 next(self.stream) 670 node = self.parse_tuple(explicit_parentheses=True) 671 self.stream.expect("rparen") 672 elif token.type == "lbracket": 673 node = self.parse_list() 674 elif token.type == "lbrace": 675 node = self.parse_dict() 676 else: 677 self.fail(f"unexpected {describe_token(token)!r}", token.lineno) 678 return node 679 680 def parse_tuple( 681 self, 682 simplified: bool = False, 683 with_condexpr: bool = True, 684 extra_end_rules: t.Optional[t.Tuple[str, ...]] = None, 685 explicit_parentheses: bool = False, 686 ) -> t.Union[nodes.Tuple, nodes.Expr]: 687 """Works like `parse_expression` but if multiple expressions are 688 delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created. 689 This method could also return a regular expression instead of a tuple 690 if no commas where found. 691 692 The default parsing mode is a full tuple. If `simplified` is `True` 693 only names and literals are parsed. The `no_condexpr` parameter is 694 forwarded to :meth:`parse_expression`. 695 696 Because tuples do not require delimiters and may end in a bogus comma 697 an extra hint is needed that marks the end of a tuple. For example 698 for loops support tuples between `for` and `in`. In that case the 699 `extra_end_rules` is set to ``['name:in']``. 700 701 `explicit_parentheses` is true if the parsing was triggered by an 702 expression in parentheses. This is used to figure out if an empty 703 tuple is a valid expression or not. 704 """ 705 lineno = self.stream.current.lineno 706 if simplified: 707 parse = self.parse_primary 708 elif with_condexpr: 709 parse = self.parse_expression 710 else: 711 712 def parse() -> nodes.Expr: 713 return self.parse_expression(with_condexpr=False) 714 715 args: t.List[nodes.Expr] = [] 716 is_tuple = False 717 718 while True: 719 if args: 720 self.stream.expect("comma") 721 if self.is_tuple_end(extra_end_rules): 722 break 723 args.append(parse()) 724 if self.stream.current.type == "comma": 725 is_tuple = True 726 else: 727 break 728 lineno = self.stream.current.lineno 729 730 if not is_tuple: 731 if args: 732 return args[0] 733 734 # if we don't have explicit parentheses, an empty tuple is 735 # not a valid expression. This would mean nothing (literally 736 # nothing) in the spot of an expression would be an empty 737 # tuple. 738 if not explicit_parentheses: 739 self.fail( 740 "Expected an expression," 741 f" got {describe_token(self.stream.current)!r}" 742 ) 743 744 return nodes.Tuple(args, "load", lineno=lineno) 745 746 def parse_list(self) -> nodes.List: 747 token = self.stream.expect("lbracket") 748 items: t.List[nodes.Expr] = [] 749 while self.stream.current.type != "rbracket": 750 if items: 751 self.stream.expect("comma") 752 if self.stream.current.type == "rbracket": 753 break 754 items.append(self.parse_expression()) 755 self.stream.expect("rbracket") 756 return nodes.List(items, lineno=token.lineno) 757 758 def parse_dict(self) -> nodes.Dict: 759 token = self.stream.expect("lbrace") 760 items: t.List[nodes.Pair] = [] 761 while self.stream.current.type != "rbrace": 762 if items: 763 self.stream.expect("comma") 764 if self.stream.current.type == "rbrace": 765 break 766 key = self.parse_expression() 767 self.stream.expect("colon") 768 value = self.parse_expression() 769 items.append(nodes.Pair(key, value, lineno=key.lineno)) 770 self.stream.expect("rbrace") 771 return nodes.Dict(items, lineno=token.lineno) 772 773 def parse_postfix(self, node: nodes.Expr) -> nodes.Expr: 774 while True: 775 token_type = self.stream.current.type 776 if token_type == "dot" or token_type == "lbracket": 777 node = self.parse_subscript(node) 778 # calls are valid both after postfix expressions (getattr 779 # and getitem) as well as filters and tests 780 elif token_type == "lparen": 781 node = self.parse_call(node) 782 else: 783 break 784 return node 785 786 def parse_filter_expr(self, node: nodes.Expr) -> nodes.Expr: 787 while True: 788 token_type = self.stream.current.type 789 if token_type == "pipe": 790 node = self.parse_filter(node) # type: ignore 791 elif token_type == "name" and self.stream.current.value == "is": 792 node = self.parse_test(node) 793 # calls are valid both after postfix expressions (getattr 794 # and getitem) as well as filters and tests 795 elif token_type == "lparen": 796 node = self.parse_call(node) 797 else: 798 break 799 return node 800 801 def parse_subscript( 802 self, node: nodes.Expr 803 ) -> t.Union[nodes.Getattr, nodes.Getitem]: 804 token = next(self.stream) 805 arg: nodes.Expr 806 807 if token.type == "dot": 808 attr_token = self.stream.current 809 next(self.stream) 810 if attr_token.type == "name": 811 return nodes.Getattr( 812 node, attr_token.value, "load", lineno=token.lineno 813 ) 814 elif attr_token.type != "integer": 815 self.fail("expected name or number", attr_token.lineno) 816 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno) 817 return nodes.Getitem(node, arg, "load", lineno=token.lineno) 818 if token.type == "lbracket": 819 args: t.List[nodes.Expr] = [] 820 while self.stream.current.type != "rbracket": 821 if args: 822 self.stream.expect("comma") 823 args.append(self.parse_subscribed()) 824 self.stream.expect("rbracket") 825 if len(args) == 1: 826 arg = args[0] 827 else: 828 arg = nodes.Tuple(args, "load", lineno=token.lineno) 829 return nodes.Getitem(node, arg, "load", lineno=token.lineno) 830 self.fail("expected subscript expression", token.lineno) 831 832 def parse_subscribed(self) -> nodes.Expr: 833 lineno = self.stream.current.lineno 834 args: t.List[t.Optional[nodes.Expr]] 835 836 if self.stream.current.type == "colon": 837 next(self.stream) 838 args = [None] 839 else: 840 node = self.parse_expression() 841 if self.stream.current.type != "colon": 842 return node 843 next(self.stream) 844 args = [node] 845 846 if self.stream.current.type == "colon": 847 args.append(None) 848 elif self.stream.current.type not in ("rbracket", "comma"): 849 args.append(self.parse_expression()) 850 else: 851 args.append(None) 852 853 if self.stream.current.type == "colon": 854 next(self.stream) 855 if self.stream.current.type not in ("rbracket", "comma"): 856 args.append(self.parse_expression()) 857 else: 858 args.append(None) 859 else: 860 args.append(None) 861 862 return nodes.Slice(lineno=lineno, *args) # noqa: B026 863 864 def parse_call_args( 865 self, 866 ) -> t.Tuple[ 867 t.List[nodes.Expr], 868 t.List[nodes.Keyword], 869 t.Optional[nodes.Expr], 870 t.Optional[nodes.Expr], 871 ]: 872 token = self.stream.expect("lparen") 873 args = [] 874 kwargs = [] 875 dyn_args = None 876 dyn_kwargs = None 877 require_comma = False 878 879 def ensure(expr: bool) -> None: 880 if not expr: 881 self.fail("invalid syntax for function call expression", token.lineno) 882 883 while self.stream.current.type != "rparen": 884 if require_comma: 885 self.stream.expect("comma") 886 887 # support for trailing comma 888 if self.stream.current.type == "rparen": 889 break 890 891 if self.stream.current.type == "mul": 892 ensure(dyn_args is None and dyn_kwargs is None) 893 next(self.stream) 894 dyn_args = self.parse_expression() 895 elif self.stream.current.type == "pow": 896 ensure(dyn_kwargs is None) 897 next(self.stream) 898 dyn_kwargs = self.parse_expression() 899 else: 900 if ( 901 self.stream.current.type == "name" 902 and self.stream.look().type == "assign" 903 ): 904 # Parsing a kwarg 905 ensure(dyn_kwargs is None) 906 key = self.stream.current.value 907 self.stream.skip(2) 908 value = self.parse_expression() 909 kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) 910 else: 911 # Parsing an arg 912 ensure(dyn_args is None and dyn_kwargs is None and not kwargs) 913 args.append(self.parse_expression()) 914 915 require_comma = True 916 917 self.stream.expect("rparen") 918 return args, kwargs, dyn_args, dyn_kwargs 919 920 def parse_call(self, node: nodes.Expr) -> nodes.Call: 921 # The lparen will be expected in parse_call_args, but the lineno 922 # needs to be recorded before the stream is advanced. 923 token = self.stream.current 924 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 925 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno) 926 927 def parse_filter( 928 self, node: t.Optional[nodes.Expr], start_inline: bool = False 929 ) -> t.Optional[nodes.Expr]: 930 while self.stream.current.type == "pipe" or start_inline: 931 if not start_inline: 932 next(self.stream) 933 token = self.stream.expect("name") 934 name = token.value 935 while self.stream.current.type == "dot": 936 next(self.stream) 937 name += "." + self.stream.expect("name").value 938 if self.stream.current.type == "lparen": 939 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 940 else: 941 args = [] 942 kwargs = [] 943 dyn_args = dyn_kwargs = None 944 node = nodes.Filter( 945 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 946 ) 947 start_inline = False 948 return node 949 950 def parse_test(self, node: nodes.Expr) -> nodes.Expr: 951 token = next(self.stream) 952 if self.stream.current.test("name:not"): 953 next(self.stream) 954 negated = True 955 else: 956 negated = False 957 name = self.stream.expect("name").value 958 while self.stream.current.type == "dot": 959 next(self.stream) 960 name += "." + self.stream.expect("name").value 961 dyn_args = dyn_kwargs = None 962 kwargs: t.List[nodes.Keyword] = [] 963 if self.stream.current.type == "lparen": 964 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 965 elif self.stream.current.type in { 966 "name", 967 "string", 968 "integer", 969 "float", 970 "lparen", 971 "lbracket", 972 "lbrace", 973 } and not self.stream.current.test_any("name:else", "name:or", "name:and"): 974 if self.stream.current.test("name:is"): 975 self.fail("You cannot chain multiple tests with is") 976 arg_node = self.parse_primary() 977 arg_node = self.parse_postfix(arg_node) 978 args = [arg_node] 979 else: 980 args = [] 981 node = nodes.Test( 982 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 983 ) 984 if negated: 985 node = nodes.Not(node, lineno=token.lineno) 986 return node 987 988 def subparse( 989 self, end_tokens: t.Optional[t.Tuple[str, ...]] = None 990 ) -> t.List[nodes.Node]: 991 body: t.List[nodes.Node] = [] 992 data_buffer: t.List[nodes.Node] = [] 993 add_data = data_buffer.append 994 995 if end_tokens is not None: 996 self._end_token_stack.append(end_tokens) 997 998 def flush_data() -> None: 999 if data_buffer: 1000 lineno = data_buffer[0].lineno 1001 body.append(nodes.Output(data_buffer[:], lineno=lineno)) 1002 del data_buffer[:] 1003 1004 try: 1005 while self.stream: 1006 token = self.stream.current 1007 if token.type == "data": 1008 if token.value: 1009 add_data(nodes.TemplateData(token.value, lineno=token.lineno)) 1010 next(self.stream) 1011 elif token.type == "variable_begin": 1012 next(self.stream) 1013 add_data(self.parse_tuple(with_condexpr=True)) 1014 self.stream.expect("variable_end") 1015 elif token.type == "block_begin": 1016 flush_data() 1017 next(self.stream) 1018 if end_tokens is not None and self.stream.current.test_any( 1019 *end_tokens 1020 ): 1021 return body 1022 rv = self.parse_statement() 1023 if isinstance(rv, list): 1024 body.extend(rv) 1025 else: 1026 body.append(rv) 1027 self.stream.expect("block_end") 1028 else: 1029 raise AssertionError("internal parsing error") 1030 1031 flush_data() 1032 finally: 1033 if end_tokens is not None: 1034 self._end_token_stack.pop() 1035 return body 1036 1037 def parse(self) -> nodes.Template: 1038 """Parse the whole template into a `Template` node.""" 1039 result = nodes.Template(self.subparse(), lineno=1) 1040 result.set_environment(self.environment) 1041 return result
49class Parser: 50 """This is the central parsing class Jinja uses. It's passed to 51 extensions and can be used to parse expressions or statements. 52 """ 53 54 def __init__( 55 self, 56 environment: "Environment", 57 source: str, 58 name: t.Optional[str] = None, 59 filename: t.Optional[str] = None, 60 state: t.Optional[str] = None, 61 ) -> None: 62 self.environment = environment 63 self.stream = environment._tokenize(source, name, filename, state) 64 self.name = name 65 self.filename = filename 66 self.closed = False 67 self.extensions: t.Dict[ 68 str, t.Callable[["Parser"], t.Union[nodes.Node, t.List[nodes.Node]]] 69 ] = {} 70 for extension in environment.iter_extensions(): 71 for tag in extension.tags: 72 self.extensions[tag] = extension.parse 73 self._last_identifier = 0 74 self._tag_stack: t.List[str] = [] 75 self._end_token_stack: t.List[t.Tuple[str, ...]] = [] 76 77 def fail( 78 self, 79 msg: str, 80 lineno: t.Optional[int] = None, 81 exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError, 82 ) -> "te.NoReturn": 83 """Convenience method that raises `exc` with the message, passed 84 line number or last line number as well as the current name and 85 filename. 86 """ 87 if lineno is None: 88 lineno = self.stream.current.lineno 89 raise exc(msg, lineno, self.name, self.filename) 90 91 def _fail_ut_eof( 92 self, 93 name: t.Optional[str], 94 end_token_stack: t.List[t.Tuple[str, ...]], 95 lineno: t.Optional[int], 96 ) -> "te.NoReturn": 97 expected: t.Set[str] = set() 98 for exprs in end_token_stack: 99 expected.update(map(describe_token_expr, exprs)) 100 if end_token_stack: 101 currently_looking: t.Optional[str] = " or ".join( 102 map(repr, map(describe_token_expr, end_token_stack[-1])) 103 ) 104 else: 105 currently_looking = None 106 107 if name is None: 108 message = ["Unexpected end of template."] 109 else: 110 message = [f"Encountered unknown tag {name!r}."] 111 112 if currently_looking: 113 if name is not None and name in expected: 114 message.append( 115 "You probably made a nesting mistake. Jinja is expecting this tag," 116 f" but currently looking for {currently_looking}." 117 ) 118 else: 119 message.append( 120 f"Jinja was looking for the following tags: {currently_looking}." 121 ) 122 123 if self._tag_stack: 124 message.append( 125 "The innermost block that needs to be closed is" 126 f" {self._tag_stack[-1]!r}." 127 ) 128 129 self.fail(" ".join(message), lineno) 130 131 def fail_unknown_tag( 132 self, name: str, lineno: t.Optional[int] = None 133 ) -> "te.NoReturn": 134 """Called if the parser encounters an unknown tag. Tries to fail 135 with a human readable error message that could help to identify 136 the problem. 137 """ 138 self._fail_ut_eof(name, self._end_token_stack, lineno) 139 140 def fail_eof( 141 self, 142 end_tokens: t.Optional[t.Tuple[str, ...]] = None, 143 lineno: t.Optional[int] = None, 144 ) -> "te.NoReturn": 145 """Like fail_unknown_tag but for end of template situations.""" 146 stack = list(self._end_token_stack) 147 if end_tokens is not None: 148 stack.append(end_tokens) 149 self._fail_ut_eof(None, stack, lineno) 150 151 def is_tuple_end( 152 self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None 153 ) -> bool: 154 """Are we at the end of a tuple?""" 155 if self.stream.current.type in ("variable_end", "block_end", "rparen"): 156 return True 157 elif extra_end_rules is not None: 158 return self.stream.current.test_any(extra_end_rules) # type: ignore 159 return False 160 161 def free_identifier(self, lineno: t.Optional[int] = None) -> nodes.InternalName: 162 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" 163 self._last_identifier += 1 164 rv = object.__new__(nodes.InternalName) 165 nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno) 166 return rv 167 168 def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]: 169 """Parse a single statement.""" 170 token = self.stream.current 171 if token.type != "name": 172 self.fail("tag name expected", token.lineno) 173 self._tag_stack.append(token.value) 174 pop_tag = True 175 try: 176 if token.value in _statement_keywords: 177 f = getattr(self, f"parse_{self.stream.current.value}") 178 return f() # type: ignore 179 if token.value == "call": 180 return self.parse_call_block() 181 if token.value == "filter": 182 return self.parse_filter_block() 183 ext = self.extensions.get(token.value) 184 if ext is not None: 185 return ext(self) 186 187 # did not work out, remove the token we pushed by accident 188 # from the stack so that the unknown tag fail function can 189 # produce a proper error message. 190 self._tag_stack.pop() 191 pop_tag = False 192 self.fail_unknown_tag(token.value, token.lineno) 193 finally: 194 if pop_tag: 195 self._tag_stack.pop() 196 197 def parse_statements( 198 self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False 199 ) -> t.List[nodes.Node]: 200 """Parse multiple statements into a list until one of the end tokens 201 is reached. This is used to parse the body of statements as it also 202 parses template data if appropriate. The parser checks first if the 203 current token is a colon and skips it if there is one. Then it checks 204 for the block end and parses until if one of the `end_tokens` is 205 reached. Per default the active token in the stream at the end of 206 the call is the matched end token. If this is not wanted `drop_needle` 207 can be set to `True` and the end token is removed. 208 """ 209 # the first token may be a colon for python compatibility 210 self.stream.skip_if("colon") 211 212 # in the future it would be possible to add whole code sections 213 # by adding some sort of end of statement token and parsing those here. 214 self.stream.expect("block_end") 215 result = self.subparse(end_tokens) 216 217 # we reached the end of the template too early, the subparser 218 # does not check for this, so we do that now 219 if self.stream.current.type == "eof": 220 self.fail_eof(end_tokens) 221 222 if drop_needle: 223 next(self.stream) 224 return result 225 226 def parse_set(self) -> t.Union[nodes.Assign, nodes.AssignBlock]: 227 """Parse an assign statement.""" 228 lineno = next(self.stream).lineno 229 target = self.parse_assign_target(with_namespace=True) 230 if self.stream.skip_if("assign"): 231 expr = self.parse_tuple() 232 return nodes.Assign(target, expr, lineno=lineno) 233 filter_node = self.parse_filter(None) 234 body = self.parse_statements(("name:endset",), drop_needle=True) 235 return nodes.AssignBlock(target, filter_node, body, lineno=lineno) 236 237 def parse_for(self) -> nodes.For: 238 """Parse a for loop.""" 239 lineno = self.stream.expect("name:for").lineno 240 target = self.parse_assign_target(extra_end_rules=("name:in",)) 241 self.stream.expect("name:in") 242 iter = self.parse_tuple( 243 with_condexpr=False, extra_end_rules=("name:recursive",) 244 ) 245 test = None 246 if self.stream.skip_if("name:if"): 247 test = self.parse_expression() 248 recursive = self.stream.skip_if("name:recursive") 249 body = self.parse_statements(("name:endfor", "name:else")) 250 if next(self.stream).value == "endfor": 251 else_ = [] 252 else: 253 else_ = self.parse_statements(("name:endfor",), drop_needle=True) 254 return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno) 255 256 def parse_if(self) -> nodes.If: 257 """Parse an if construct.""" 258 node = result = nodes.If(lineno=self.stream.expect("name:if").lineno) 259 while True: 260 node.test = self.parse_tuple(with_condexpr=False) 261 node.body = self.parse_statements(("name:elif", "name:else", "name:endif")) 262 node.elif_ = [] 263 node.else_ = [] 264 token = next(self.stream) 265 if token.test("name:elif"): 266 node = nodes.If(lineno=self.stream.current.lineno) 267 result.elif_.append(node) 268 continue 269 elif token.test("name:else"): 270 result.else_ = self.parse_statements(("name:endif",), drop_needle=True) 271 break 272 return result 273 274 def parse_with(self) -> nodes.With: 275 node = nodes.With(lineno=next(self.stream).lineno) 276 targets: t.List[nodes.Expr] = [] 277 values: t.List[nodes.Expr] = [] 278 while self.stream.current.type != "block_end": 279 if targets: 280 self.stream.expect("comma") 281 target = self.parse_assign_target() 282 target.set_ctx("param") 283 targets.append(target) 284 self.stream.expect("assign") 285 values.append(self.parse_expression()) 286 node.targets = targets 287 node.values = values 288 node.body = self.parse_statements(("name:endwith",), drop_needle=True) 289 return node 290 291 def parse_autoescape(self) -> nodes.Scope: 292 node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno) 293 node.options = [nodes.Keyword("autoescape", self.parse_expression())] 294 node.body = self.parse_statements(("name:endautoescape",), drop_needle=True) 295 return nodes.Scope([node]) 296 297 def parse_block(self) -> nodes.Block: 298 node = nodes.Block(lineno=next(self.stream).lineno) 299 node.name = self.stream.expect("name").value 300 node.scoped = self.stream.skip_if("name:scoped") 301 node.required = self.stream.skip_if("name:required") 302 303 # common problem people encounter when switching from django 304 # to jinja. we do not support hyphens in block names, so let's 305 # raise a nicer error message in that case. 306 if self.stream.current.type == "sub": 307 self.fail( 308 "Block names in Jinja have to be valid Python identifiers and may not" 309 " contain hyphens, use an underscore instead." 310 ) 311 312 node.body = self.parse_statements(("name:endblock",), drop_needle=True) 313 314 # enforce that required blocks only contain whitespace or comments 315 # by asserting that the body, if not empty, is just TemplateData nodes 316 # with whitespace data 317 if node.required: 318 for body_node in node.body: 319 if not isinstance(body_node, nodes.Output) or any( 320 not isinstance(output_node, nodes.TemplateData) 321 or not output_node.data.isspace() 322 for output_node in body_node.nodes 323 ): 324 self.fail("Required blocks can only contain comments or whitespace") 325 326 self.stream.skip_if("name:" + node.name) 327 return node 328 329 def parse_extends(self) -> nodes.Extends: 330 node = nodes.Extends(lineno=next(self.stream).lineno) 331 node.template = self.parse_expression() 332 return node 333 334 def parse_import_context( 335 self, node: _ImportInclude, default: bool 336 ) -> _ImportInclude: 337 if self.stream.current.test_any( 338 "name:with", "name:without" 339 ) and self.stream.look().test("name:context"): 340 node.with_context = next(self.stream).value == "with" 341 self.stream.skip() 342 else: 343 node.with_context = default 344 return node 345 346 def parse_include(self) -> nodes.Include: 347 node = nodes.Include(lineno=next(self.stream).lineno) 348 node.template = self.parse_expression() 349 if self.stream.current.test("name:ignore") and self.stream.look().test( 350 "name:missing" 351 ): 352 node.ignore_missing = True 353 self.stream.skip(2) 354 else: 355 node.ignore_missing = False 356 return self.parse_import_context(node, True) 357 358 def parse_import(self) -> nodes.Import: 359 node = nodes.Import(lineno=next(self.stream).lineno) 360 node.template = self.parse_expression() 361 self.stream.expect("name:as") 362 node.target = self.parse_assign_target(name_only=True).name 363 return self.parse_import_context(node, False) 364 365 def parse_from(self) -> nodes.FromImport: 366 node = nodes.FromImport(lineno=next(self.stream).lineno) 367 node.template = self.parse_expression() 368 self.stream.expect("name:import") 369 node.names = [] 370 371 def parse_context() -> bool: 372 if self.stream.current.value in { 373 "with", 374 "without", 375 } and self.stream.look().test("name:context"): 376 node.with_context = next(self.stream).value == "with" 377 self.stream.skip() 378 return True 379 return False 380 381 while True: 382 if node.names: 383 self.stream.expect("comma") 384 if self.stream.current.type == "name": 385 if parse_context(): 386 break 387 target = self.parse_assign_target(name_only=True) 388 if target.name.startswith("_"): 389 self.fail( 390 "names starting with an underline can not be imported", 391 target.lineno, 392 exc=TemplateAssertionError, 393 ) 394 if self.stream.skip_if("name:as"): 395 alias = self.parse_assign_target(name_only=True) 396 node.names.append((target.name, alias.name)) 397 else: 398 node.names.append(target.name) 399 if parse_context() or self.stream.current.type != "comma": 400 break 401 else: 402 self.stream.expect("name") 403 if not hasattr(node, "with_context"): 404 node.with_context = False 405 return node 406 407 def parse_signature(self, node: _MacroCall) -> None: 408 args = node.args = [] 409 defaults = node.defaults = [] 410 self.stream.expect("lparen") 411 while self.stream.current.type != "rparen": 412 if args: 413 self.stream.expect("comma") 414 arg = self.parse_assign_target(name_only=True) 415 arg.set_ctx("param") 416 if self.stream.skip_if("assign"): 417 defaults.append(self.parse_expression()) 418 elif defaults: 419 self.fail("non-default argument follows default argument") 420 args.append(arg) 421 self.stream.expect("rparen") 422 423 def parse_call_block(self) -> nodes.CallBlock: 424 node = nodes.CallBlock(lineno=next(self.stream).lineno) 425 if self.stream.current.type == "lparen": 426 self.parse_signature(node) 427 else: 428 node.args = [] 429 node.defaults = [] 430 431 call_node = self.parse_expression() 432 if not isinstance(call_node, nodes.Call): 433 self.fail("expected call", node.lineno) 434 node.call = call_node 435 node.body = self.parse_statements(("name:endcall",), drop_needle=True) 436 return node 437 438 def parse_filter_block(self) -> nodes.FilterBlock: 439 node = nodes.FilterBlock(lineno=next(self.stream).lineno) 440 node.filter = self.parse_filter(None, start_inline=True) # type: ignore 441 node.body = self.parse_statements(("name:endfilter",), drop_needle=True) 442 return node 443 444 def parse_macro(self) -> nodes.Macro: 445 node = nodes.Macro(lineno=next(self.stream).lineno) 446 node.name = self.parse_assign_target(name_only=True).name 447 self.parse_signature(node) 448 node.body = self.parse_statements(("name:endmacro",), drop_needle=True) 449 return node 450 451 def parse_print(self) -> nodes.Output: 452 node = nodes.Output(lineno=next(self.stream).lineno) 453 node.nodes = [] 454 while self.stream.current.type != "block_end": 455 if node.nodes: 456 self.stream.expect("comma") 457 node.nodes.append(self.parse_expression()) 458 return node 459 460 @typing.overload 461 def parse_assign_target( 462 self, with_tuple: bool = ..., name_only: "te.Literal[True]" = ... 463 ) -> nodes.Name: ... 464 465 @typing.overload 466 def parse_assign_target( 467 self, 468 with_tuple: bool = True, 469 name_only: bool = False, 470 extra_end_rules: t.Optional[t.Tuple[str, ...]] = None, 471 with_namespace: bool = False, 472 ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]: ... 473 474 def parse_assign_target( 475 self, 476 with_tuple: bool = True, 477 name_only: bool = False, 478 extra_end_rules: t.Optional[t.Tuple[str, ...]] = None, 479 with_namespace: bool = False, 480 ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]: 481 """Parse an assignment target. As Jinja allows assignments to 482 tuples, this function can parse all allowed assignment targets. Per 483 default assignments to tuples are parsed, that can be disable however 484 by setting `with_tuple` to `False`. If only assignments to names are 485 wanted `name_only` can be set to `True`. The `extra_end_rules` 486 parameter is forwarded to the tuple parsing function. If 487 `with_namespace` is enabled, a namespace assignment may be parsed. 488 """ 489 target: nodes.Expr 490 491 if with_namespace and self.stream.look().type == "dot": 492 token = self.stream.expect("name") 493 next(self.stream) # dot 494 attr = self.stream.expect("name") 495 target = nodes.NSRef(token.value, attr.value, lineno=token.lineno) 496 elif name_only: 497 token = self.stream.expect("name") 498 target = nodes.Name(token.value, "store", lineno=token.lineno) 499 else: 500 if with_tuple: 501 target = self.parse_tuple( 502 simplified=True, extra_end_rules=extra_end_rules 503 ) 504 else: 505 target = self.parse_primary() 506 507 target.set_ctx("store") 508 509 if not target.can_assign(): 510 self.fail( 511 f"can't assign to {type(target).__name__.lower()!r}", target.lineno 512 ) 513 514 return target # type: ignore 515 516 def parse_expression(self, with_condexpr: bool = True) -> nodes.Expr: 517 """Parse an expression. Per default all expressions are parsed, if 518 the optional `with_condexpr` parameter is set to `False` conditional 519 expressions are not parsed. 520 """ 521 if with_condexpr: 522 return self.parse_condexpr() 523 return self.parse_or() 524 525 def parse_condexpr(self) -> nodes.Expr: 526 lineno = self.stream.current.lineno 527 expr1 = self.parse_or() 528 expr3: t.Optional[nodes.Expr] 529 530 while self.stream.skip_if("name:if"): 531 expr2 = self.parse_or() 532 if self.stream.skip_if("name:else"): 533 expr3 = self.parse_condexpr() 534 else: 535 expr3 = None 536 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno) 537 lineno = self.stream.current.lineno 538 return expr1 539 540 def parse_or(self) -> nodes.Expr: 541 lineno = self.stream.current.lineno 542 left = self.parse_and() 543 while self.stream.skip_if("name:or"): 544 right = self.parse_and() 545 left = nodes.Or(left, right, lineno=lineno) 546 lineno = self.stream.current.lineno 547 return left 548 549 def parse_and(self) -> nodes.Expr: 550 lineno = self.stream.current.lineno 551 left = self.parse_not() 552 while self.stream.skip_if("name:and"): 553 right = self.parse_not() 554 left = nodes.And(left, right, lineno=lineno) 555 lineno = self.stream.current.lineno 556 return left 557 558 def parse_not(self) -> nodes.Expr: 559 if self.stream.current.test("name:not"): 560 lineno = next(self.stream).lineno 561 return nodes.Not(self.parse_not(), lineno=lineno) 562 return self.parse_compare() 563 564 def parse_compare(self) -> nodes.Expr: 565 lineno = self.stream.current.lineno 566 expr = self.parse_math1() 567 ops = [] 568 while True: 569 token_type = self.stream.current.type 570 if token_type in _compare_operators: 571 next(self.stream) 572 ops.append(nodes.Operand(token_type, self.parse_math1())) 573 elif self.stream.skip_if("name:in"): 574 ops.append(nodes.Operand("in", self.parse_math1())) 575 elif self.stream.current.test("name:not") and self.stream.look().test( 576 "name:in" 577 ): 578 self.stream.skip(2) 579 ops.append(nodes.Operand("notin", self.parse_math1())) 580 else: 581 break 582 lineno = self.stream.current.lineno 583 if not ops: 584 return expr 585 return nodes.Compare(expr, ops, lineno=lineno) 586 587 def parse_math1(self) -> nodes.Expr: 588 lineno = self.stream.current.lineno 589 left = self.parse_concat() 590 while self.stream.current.type in ("add", "sub"): 591 cls = _math_nodes[self.stream.current.type] 592 next(self.stream) 593 right = self.parse_concat() 594 left = cls(left, right, lineno=lineno) 595 lineno = self.stream.current.lineno 596 return left 597 598 def parse_concat(self) -> nodes.Expr: 599 lineno = self.stream.current.lineno 600 args = [self.parse_math2()] 601 while self.stream.current.type == "tilde": 602 next(self.stream) 603 args.append(self.parse_math2()) 604 if len(args) == 1: 605 return args[0] 606 return nodes.Concat(args, lineno=lineno) 607 608 def parse_math2(self) -> nodes.Expr: 609 lineno = self.stream.current.lineno 610 left = self.parse_pow() 611 while self.stream.current.type in ("mul", "div", "floordiv", "mod"): 612 cls = _math_nodes[self.stream.current.type] 613 next(self.stream) 614 right = self.parse_pow() 615 left = cls(left, right, lineno=lineno) 616 lineno = self.stream.current.lineno 617 return left 618 619 def parse_pow(self) -> nodes.Expr: 620 lineno = self.stream.current.lineno 621 left = self.parse_unary() 622 while self.stream.current.type == "pow": 623 next(self.stream) 624 right = self.parse_unary() 625 left = nodes.Pow(left, right, lineno=lineno) 626 lineno = self.stream.current.lineno 627 return left 628 629 def parse_unary(self, with_filter: bool = True) -> nodes.Expr: 630 token_type = self.stream.current.type 631 lineno = self.stream.current.lineno 632 node: nodes.Expr 633 634 if token_type == "sub": 635 next(self.stream) 636 node = nodes.Neg(self.parse_unary(False), lineno=lineno) 637 elif token_type == "add": 638 next(self.stream) 639 node = nodes.Pos(self.parse_unary(False), lineno=lineno) 640 else: 641 node = self.parse_primary() 642 node = self.parse_postfix(node) 643 if with_filter: 644 node = self.parse_filter_expr(node) 645 return node 646 647 def parse_primary(self) -> nodes.Expr: 648 token = self.stream.current 649 node: nodes.Expr 650 if token.type == "name": 651 if token.value in ("true", "false", "True", "False"): 652 node = nodes.Const(token.value in ("true", "True"), lineno=token.lineno) 653 elif token.value in ("none", "None"): 654 node = nodes.Const(None, lineno=token.lineno) 655 else: 656 node = nodes.Name(token.value, "load", lineno=token.lineno) 657 next(self.stream) 658 elif token.type == "string": 659 next(self.stream) 660 buf = [token.value] 661 lineno = token.lineno 662 while self.stream.current.type == "string": 663 buf.append(self.stream.current.value) 664 next(self.stream) 665 node = nodes.Const("".join(buf), lineno=lineno) 666 elif token.type in ("integer", "float"): 667 next(self.stream) 668 node = nodes.Const(token.value, lineno=token.lineno) 669 elif token.type == "lparen": 670 next(self.stream) 671 node = self.parse_tuple(explicit_parentheses=True) 672 self.stream.expect("rparen") 673 elif token.type == "lbracket": 674 node = self.parse_list() 675 elif token.type == "lbrace": 676 node = self.parse_dict() 677 else: 678 self.fail(f"unexpected {describe_token(token)!r}", token.lineno) 679 return node 680 681 def parse_tuple( 682 self, 683 simplified: bool = False, 684 with_condexpr: bool = True, 685 extra_end_rules: t.Optional[t.Tuple[str, ...]] = None, 686 explicit_parentheses: bool = False, 687 ) -> t.Union[nodes.Tuple, nodes.Expr]: 688 """Works like `parse_expression` but if multiple expressions are 689 delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created. 690 This method could also return a regular expression instead of a tuple 691 if no commas where found. 692 693 The default parsing mode is a full tuple. If `simplified` is `True` 694 only names and literals are parsed. The `no_condexpr` parameter is 695 forwarded to :meth:`parse_expression`. 696 697 Because tuples do not require delimiters and may end in a bogus comma 698 an extra hint is needed that marks the end of a tuple. For example 699 for loops support tuples between `for` and `in`. In that case the 700 `extra_end_rules` is set to ``['name:in']``. 701 702 `explicit_parentheses` is true if the parsing was triggered by an 703 expression in parentheses. This is used to figure out if an empty 704 tuple is a valid expression or not. 705 """ 706 lineno = self.stream.current.lineno 707 if simplified: 708 parse = self.parse_primary 709 elif with_condexpr: 710 parse = self.parse_expression 711 else: 712 713 def parse() -> nodes.Expr: 714 return self.parse_expression(with_condexpr=False) 715 716 args: t.List[nodes.Expr] = [] 717 is_tuple = False 718 719 while True: 720 if args: 721 self.stream.expect("comma") 722 if self.is_tuple_end(extra_end_rules): 723 break 724 args.append(parse()) 725 if self.stream.current.type == "comma": 726 is_tuple = True 727 else: 728 break 729 lineno = self.stream.current.lineno 730 731 if not is_tuple: 732 if args: 733 return args[0] 734 735 # if we don't have explicit parentheses, an empty tuple is 736 # not a valid expression. This would mean nothing (literally 737 # nothing) in the spot of an expression would be an empty 738 # tuple. 739 if not explicit_parentheses: 740 self.fail( 741 "Expected an expression," 742 f" got {describe_token(self.stream.current)!r}" 743 ) 744 745 return nodes.Tuple(args, "load", lineno=lineno) 746 747 def parse_list(self) -> nodes.List: 748 token = self.stream.expect("lbracket") 749 items: t.List[nodes.Expr] = [] 750 while self.stream.current.type != "rbracket": 751 if items: 752 self.stream.expect("comma") 753 if self.stream.current.type == "rbracket": 754 break 755 items.append(self.parse_expression()) 756 self.stream.expect("rbracket") 757 return nodes.List(items, lineno=token.lineno) 758 759 def parse_dict(self) -> nodes.Dict: 760 token = self.stream.expect("lbrace") 761 items: t.List[nodes.Pair] = [] 762 while self.stream.current.type != "rbrace": 763 if items: 764 self.stream.expect("comma") 765 if self.stream.current.type == "rbrace": 766 break 767 key = self.parse_expression() 768 self.stream.expect("colon") 769 value = self.parse_expression() 770 items.append(nodes.Pair(key, value, lineno=key.lineno)) 771 self.stream.expect("rbrace") 772 return nodes.Dict(items, lineno=token.lineno) 773 774 def parse_postfix(self, node: nodes.Expr) -> nodes.Expr: 775 while True: 776 token_type = self.stream.current.type 777 if token_type == "dot" or token_type == "lbracket": 778 node = self.parse_subscript(node) 779 # calls are valid both after postfix expressions (getattr 780 # and getitem) as well as filters and tests 781 elif token_type == "lparen": 782 node = self.parse_call(node) 783 else: 784 break 785 return node 786 787 def parse_filter_expr(self, node: nodes.Expr) -> nodes.Expr: 788 while True: 789 token_type = self.stream.current.type 790 if token_type == "pipe": 791 node = self.parse_filter(node) # type: ignore 792 elif token_type == "name" and self.stream.current.value == "is": 793 node = self.parse_test(node) 794 # calls are valid both after postfix expressions (getattr 795 # and getitem) as well as filters and tests 796 elif token_type == "lparen": 797 node = self.parse_call(node) 798 else: 799 break 800 return node 801 802 def parse_subscript( 803 self, node: nodes.Expr 804 ) -> t.Union[nodes.Getattr, nodes.Getitem]: 805 token = next(self.stream) 806 arg: nodes.Expr 807 808 if token.type == "dot": 809 attr_token = self.stream.current 810 next(self.stream) 811 if attr_token.type == "name": 812 return nodes.Getattr( 813 node, attr_token.value, "load", lineno=token.lineno 814 ) 815 elif attr_token.type != "integer": 816 self.fail("expected name or number", attr_token.lineno) 817 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno) 818 return nodes.Getitem(node, arg, "load", lineno=token.lineno) 819 if token.type == "lbracket": 820 args: t.List[nodes.Expr] = [] 821 while self.stream.current.type != "rbracket": 822 if args: 823 self.stream.expect("comma") 824 args.append(self.parse_subscribed()) 825 self.stream.expect("rbracket") 826 if len(args) == 1: 827 arg = args[0] 828 else: 829 arg = nodes.Tuple(args, "load", lineno=token.lineno) 830 return nodes.Getitem(node, arg, "load", lineno=token.lineno) 831 self.fail("expected subscript expression", token.lineno) 832 833 def parse_subscribed(self) -> nodes.Expr: 834 lineno = self.stream.current.lineno 835 args: t.List[t.Optional[nodes.Expr]] 836 837 if self.stream.current.type == "colon": 838 next(self.stream) 839 args = [None] 840 else: 841 node = self.parse_expression() 842 if self.stream.current.type != "colon": 843 return node 844 next(self.stream) 845 args = [node] 846 847 if self.stream.current.type == "colon": 848 args.append(None) 849 elif self.stream.current.type not in ("rbracket", "comma"): 850 args.append(self.parse_expression()) 851 else: 852 args.append(None) 853 854 if self.stream.current.type == "colon": 855 next(self.stream) 856 if self.stream.current.type not in ("rbracket", "comma"): 857 args.append(self.parse_expression()) 858 else: 859 args.append(None) 860 else: 861 args.append(None) 862 863 return nodes.Slice(lineno=lineno, *args) # noqa: B026 864 865 def parse_call_args( 866 self, 867 ) -> t.Tuple[ 868 t.List[nodes.Expr], 869 t.List[nodes.Keyword], 870 t.Optional[nodes.Expr], 871 t.Optional[nodes.Expr], 872 ]: 873 token = self.stream.expect("lparen") 874 args = [] 875 kwargs = [] 876 dyn_args = None 877 dyn_kwargs = None 878 require_comma = False 879 880 def ensure(expr: bool) -> None: 881 if not expr: 882 self.fail("invalid syntax for function call expression", token.lineno) 883 884 while self.stream.current.type != "rparen": 885 if require_comma: 886 self.stream.expect("comma") 887 888 # support for trailing comma 889 if self.stream.current.type == "rparen": 890 break 891 892 if self.stream.current.type == "mul": 893 ensure(dyn_args is None and dyn_kwargs is None) 894 next(self.stream) 895 dyn_args = self.parse_expression() 896 elif self.stream.current.type == "pow": 897 ensure(dyn_kwargs is None) 898 next(self.stream) 899 dyn_kwargs = self.parse_expression() 900 else: 901 if ( 902 self.stream.current.type == "name" 903 and self.stream.look().type == "assign" 904 ): 905 # Parsing a kwarg 906 ensure(dyn_kwargs is None) 907 key = self.stream.current.value 908 self.stream.skip(2) 909 value = self.parse_expression() 910 kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) 911 else: 912 # Parsing an arg 913 ensure(dyn_args is None and dyn_kwargs is None and not kwargs) 914 args.append(self.parse_expression()) 915 916 require_comma = True 917 918 self.stream.expect("rparen") 919 return args, kwargs, dyn_args, dyn_kwargs 920 921 def parse_call(self, node: nodes.Expr) -> nodes.Call: 922 # The lparen will be expected in parse_call_args, but the lineno 923 # needs to be recorded before the stream is advanced. 924 token = self.stream.current 925 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 926 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno) 927 928 def parse_filter( 929 self, node: t.Optional[nodes.Expr], start_inline: bool = False 930 ) -> t.Optional[nodes.Expr]: 931 while self.stream.current.type == "pipe" or start_inline: 932 if not start_inline: 933 next(self.stream) 934 token = self.stream.expect("name") 935 name = token.value 936 while self.stream.current.type == "dot": 937 next(self.stream) 938 name += "." + self.stream.expect("name").value 939 if self.stream.current.type == "lparen": 940 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 941 else: 942 args = [] 943 kwargs = [] 944 dyn_args = dyn_kwargs = None 945 node = nodes.Filter( 946 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 947 ) 948 start_inline = False 949 return node 950 951 def parse_test(self, node: nodes.Expr) -> nodes.Expr: 952 token = next(self.stream) 953 if self.stream.current.test("name:not"): 954 next(self.stream) 955 negated = True 956 else: 957 negated = False 958 name = self.stream.expect("name").value 959 while self.stream.current.type == "dot": 960 next(self.stream) 961 name += "." + self.stream.expect("name").value 962 dyn_args = dyn_kwargs = None 963 kwargs: t.List[nodes.Keyword] = [] 964 if self.stream.current.type == "lparen": 965 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 966 elif self.stream.current.type in { 967 "name", 968 "string", 969 "integer", 970 "float", 971 "lparen", 972 "lbracket", 973 "lbrace", 974 } and not self.stream.current.test_any("name:else", "name:or", "name:and"): 975 if self.stream.current.test("name:is"): 976 self.fail("You cannot chain multiple tests with is") 977 arg_node = self.parse_primary() 978 arg_node = self.parse_postfix(arg_node) 979 args = [arg_node] 980 else: 981 args = [] 982 node = nodes.Test( 983 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 984 ) 985 if negated: 986 node = nodes.Not(node, lineno=token.lineno) 987 return node 988 989 def subparse( 990 self, end_tokens: t.Optional[t.Tuple[str, ...]] = None 991 ) -> t.List[nodes.Node]: 992 body: t.List[nodes.Node] = [] 993 data_buffer: t.List[nodes.Node] = [] 994 add_data = data_buffer.append 995 996 if end_tokens is not None: 997 self._end_token_stack.append(end_tokens) 998 999 def flush_data() -> None: 1000 if data_buffer: 1001 lineno = data_buffer[0].lineno 1002 body.append(nodes.Output(data_buffer[:], lineno=lineno)) 1003 del data_buffer[:] 1004 1005 try: 1006 while self.stream: 1007 token = self.stream.current 1008 if token.type == "data": 1009 if token.value: 1010 add_data(nodes.TemplateData(token.value, lineno=token.lineno)) 1011 next(self.stream) 1012 elif token.type == "variable_begin": 1013 next(self.stream) 1014 add_data(self.parse_tuple(with_condexpr=True)) 1015 self.stream.expect("variable_end") 1016 elif token.type == "block_begin": 1017 flush_data() 1018 next(self.stream) 1019 if end_tokens is not None and self.stream.current.test_any( 1020 *end_tokens 1021 ): 1022 return body 1023 rv = self.parse_statement() 1024 if isinstance(rv, list): 1025 body.extend(rv) 1026 else: 1027 body.append(rv) 1028 self.stream.expect("block_end") 1029 else: 1030 raise AssertionError("internal parsing error") 1031 1032 flush_data() 1033 finally: 1034 if end_tokens is not None: 1035 self._end_token_stack.pop() 1036 return body 1037 1038 def parse(self) -> nodes.Template: 1039 """Parse the whole template into a `Template` node.""" 1040 result = nodes.Template(self.subparse(), lineno=1) 1041 result.set_environment(self.environment) 1042 return result
This is the central parsing class Jinja uses. It's passed to extensions and can be used to parse expressions or statements.
54 def __init__( 55 self, 56 environment: "Environment", 57 source: str, 58 name: t.Optional[str] = None, 59 filename: t.Optional[str] = None, 60 state: t.Optional[str] = None, 61 ) -> None: 62 self.environment = environment 63 self.stream = environment._tokenize(source, name, filename, state) 64 self.name = name 65 self.filename = filename 66 self.closed = False 67 self.extensions: t.Dict[ 68 str, t.Callable[["Parser"], t.Union[nodes.Node, t.List[nodes.Node]]] 69 ] = {} 70 for extension in environment.iter_extensions(): 71 for tag in extension.tags: 72 self.extensions[tag] = extension.parse 73 self._last_identifier = 0 74 self._tag_stack: t.List[str] = [] 75 self._end_token_stack: t.List[t.Tuple[str, ...]] = []
77 def fail( 78 self, 79 msg: str, 80 lineno: t.Optional[int] = None, 81 exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError, 82 ) -> "te.NoReturn": 83 """Convenience method that raises `exc` with the message, passed 84 line number or last line number as well as the current name and 85 filename. 86 """ 87 if lineno is None: 88 lineno = self.stream.current.lineno 89 raise exc(msg, lineno, self.name, self.filename)
Convenience method that raises exc
with the message, passed
line number or last line number as well as the current name and
filename.
131 def fail_unknown_tag( 132 self, name: str, lineno: t.Optional[int] = None 133 ) -> "te.NoReturn": 134 """Called if the parser encounters an unknown tag. Tries to fail 135 with a human readable error message that could help to identify 136 the problem. 137 """ 138 self._fail_ut_eof(name, self._end_token_stack, lineno)
Called if the parser encounters an unknown tag. Tries to fail with a human readable error message that could help to identify the problem.
140 def fail_eof( 141 self, 142 end_tokens: t.Optional[t.Tuple[str, ...]] = None, 143 lineno: t.Optional[int] = None, 144 ) -> "te.NoReturn": 145 """Like fail_unknown_tag but for end of template situations.""" 146 stack = list(self._end_token_stack) 147 if end_tokens is not None: 148 stack.append(end_tokens) 149 self._fail_ut_eof(None, stack, lineno)
Like fail_unknown_tag but for end of template situations.
151 def is_tuple_end( 152 self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None 153 ) -> bool: 154 """Are we at the end of a tuple?""" 155 if self.stream.current.type in ("variable_end", "block_end", "rparen"): 156 return True 157 elif extra_end_rules is not None: 158 return self.stream.current.test_any(extra_end_rules) # type: ignore 159 return False
Are we at the end of a tuple?
161 def free_identifier(self, lineno: t.Optional[int] = None) -> nodes.InternalName: 162 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" 163 self._last_identifier += 1 164 rv = object.__new__(nodes.InternalName) 165 nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno) 166 return rv
Return a new free identifier as ~jinja2.nodes.InternalName
.
168 def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]: 169 """Parse a single statement.""" 170 token = self.stream.current 171 if token.type != "name": 172 self.fail("tag name expected", token.lineno) 173 self._tag_stack.append(token.value) 174 pop_tag = True 175 try: 176 if token.value in _statement_keywords: 177 f = getattr(self, f"parse_{self.stream.current.value}") 178 return f() # type: ignore 179 if token.value == "call": 180 return self.parse_call_block() 181 if token.value == "filter": 182 return self.parse_filter_block() 183 ext = self.extensions.get(token.value) 184 if ext is not None: 185 return ext(self) 186 187 # did not work out, remove the token we pushed by accident 188 # from the stack so that the unknown tag fail function can 189 # produce a proper error message. 190 self._tag_stack.pop() 191 pop_tag = False 192 self.fail_unknown_tag(token.value, token.lineno) 193 finally: 194 if pop_tag: 195 self._tag_stack.pop()
Parse a single statement.
197 def parse_statements( 198 self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False 199 ) -> t.List[nodes.Node]: 200 """Parse multiple statements into a list until one of the end tokens 201 is reached. This is used to parse the body of statements as it also 202 parses template data if appropriate. The parser checks first if the 203 current token is a colon and skips it if there is one. Then it checks 204 for the block end and parses until if one of the `end_tokens` is 205 reached. Per default the active token in the stream at the end of 206 the call is the matched end token. If this is not wanted `drop_needle` 207 can be set to `True` and the end token is removed. 208 """ 209 # the first token may be a colon for python compatibility 210 self.stream.skip_if("colon") 211 212 # in the future it would be possible to add whole code sections 213 # by adding some sort of end of statement token and parsing those here. 214 self.stream.expect("block_end") 215 result = self.subparse(end_tokens) 216 217 # we reached the end of the template too early, the subparser 218 # does not check for this, so we do that now 219 if self.stream.current.type == "eof": 220 self.fail_eof(end_tokens) 221 222 if drop_needle: 223 next(self.stream) 224 return result
Parse multiple statements into a list until one of the end tokens
is reached. This is used to parse the body of statements as it also
parses template data if appropriate. The parser checks first if the
current token is a colon and skips it if there is one. Then it checks
for the block end and parses until if one of the end_tokens
is
reached. Per default the active token in the stream at the end of
the call is the matched end token. If this is not wanted drop_needle
can be set to True
and the end token is removed.
226 def parse_set(self) -> t.Union[nodes.Assign, nodes.AssignBlock]: 227 """Parse an assign statement.""" 228 lineno = next(self.stream).lineno 229 target = self.parse_assign_target(with_namespace=True) 230 if self.stream.skip_if("assign"): 231 expr = self.parse_tuple() 232 return nodes.Assign(target, expr, lineno=lineno) 233 filter_node = self.parse_filter(None) 234 body = self.parse_statements(("name:endset",), drop_needle=True) 235 return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
Parse an assign statement.
237 def parse_for(self) -> nodes.For: 238 """Parse a for loop.""" 239 lineno = self.stream.expect("name:for").lineno 240 target = self.parse_assign_target(extra_end_rules=("name:in",)) 241 self.stream.expect("name:in") 242 iter = self.parse_tuple( 243 with_condexpr=False, extra_end_rules=("name:recursive",) 244 ) 245 test = None 246 if self.stream.skip_if("name:if"): 247 test = self.parse_expression() 248 recursive = self.stream.skip_if("name:recursive") 249 body = self.parse_statements(("name:endfor", "name:else")) 250 if next(self.stream).value == "endfor": 251 else_ = [] 252 else: 253 else_ = self.parse_statements(("name:endfor",), drop_needle=True) 254 return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno)
Parse a for loop.
256 def parse_if(self) -> nodes.If: 257 """Parse an if construct.""" 258 node = result = nodes.If(lineno=self.stream.expect("name:if").lineno) 259 while True: 260 node.test = self.parse_tuple(with_condexpr=False) 261 node.body = self.parse_statements(("name:elif", "name:else", "name:endif")) 262 node.elif_ = [] 263 node.else_ = [] 264 token = next(self.stream) 265 if token.test("name:elif"): 266 node = nodes.If(lineno=self.stream.current.lineno) 267 result.elif_.append(node) 268 continue 269 elif token.test("name:else"): 270 result.else_ = self.parse_statements(("name:endif",), drop_needle=True) 271 break 272 return result
Parse an if construct.
274 def parse_with(self) -> nodes.With: 275 node = nodes.With(lineno=next(self.stream).lineno) 276 targets: t.List[nodes.Expr] = [] 277 values: t.List[nodes.Expr] = [] 278 while self.stream.current.type != "block_end": 279 if targets: 280 self.stream.expect("comma") 281 target = self.parse_assign_target() 282 target.set_ctx("param") 283 targets.append(target) 284 self.stream.expect("assign") 285 values.append(self.parse_expression()) 286 node.targets = targets 287 node.values = values 288 node.body = self.parse_statements(("name:endwith",), drop_needle=True) 289 return node
291 def parse_autoescape(self) -> nodes.Scope: 292 node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno) 293 node.options = [nodes.Keyword("autoescape", self.parse_expression())] 294 node.body = self.parse_statements(("name:endautoescape",), drop_needle=True) 295 return nodes.Scope([node])
297 def parse_block(self) -> nodes.Block: 298 node = nodes.Block(lineno=next(self.stream).lineno) 299 node.name = self.stream.expect("name").value 300 node.scoped = self.stream.skip_if("name:scoped") 301 node.required = self.stream.skip_if("name:required") 302 303 # common problem people encounter when switching from django 304 # to jinja. we do not support hyphens in block names, so let's 305 # raise a nicer error message in that case. 306 if self.stream.current.type == "sub": 307 self.fail( 308 "Block names in Jinja have to be valid Python identifiers and may not" 309 " contain hyphens, use an underscore instead." 310 ) 311 312 node.body = self.parse_statements(("name:endblock",), drop_needle=True) 313 314 # enforce that required blocks only contain whitespace or comments 315 # by asserting that the body, if not empty, is just TemplateData nodes 316 # with whitespace data 317 if node.required: 318 for body_node in node.body: 319 if not isinstance(body_node, nodes.Output) or any( 320 not isinstance(output_node, nodes.TemplateData) 321 or not output_node.data.isspace() 322 for output_node in body_node.nodes 323 ): 324 self.fail("Required blocks can only contain comments or whitespace") 325 326 self.stream.skip_if("name:" + node.name) 327 return node
334 def parse_import_context( 335 self, node: _ImportInclude, default: bool 336 ) -> _ImportInclude: 337 if self.stream.current.test_any( 338 "name:with", "name:without" 339 ) and self.stream.look().test("name:context"): 340 node.with_context = next(self.stream).value == "with" 341 self.stream.skip() 342 else: 343 node.with_context = default 344 return node
346 def parse_include(self) -> nodes.Include: 347 node = nodes.Include(lineno=next(self.stream).lineno) 348 node.template = self.parse_expression() 349 if self.stream.current.test("name:ignore") and self.stream.look().test( 350 "name:missing" 351 ): 352 node.ignore_missing = True 353 self.stream.skip(2) 354 else: 355 node.ignore_missing = False 356 return self.parse_import_context(node, True)
365 def parse_from(self) -> nodes.FromImport: 366 node = nodes.FromImport(lineno=next(self.stream).lineno) 367 node.template = self.parse_expression() 368 self.stream.expect("name:import") 369 node.names = [] 370 371 def parse_context() -> bool: 372 if self.stream.current.value in { 373 "with", 374 "without", 375 } and self.stream.look().test("name:context"): 376 node.with_context = next(self.stream).value == "with" 377 self.stream.skip() 378 return True 379 return False 380 381 while True: 382 if node.names: 383 self.stream.expect("comma") 384 if self.stream.current.type == "name": 385 if parse_context(): 386 break 387 target = self.parse_assign_target(name_only=True) 388 if target.name.startswith("_"): 389 self.fail( 390 "names starting with an underline can not be imported", 391 target.lineno, 392 exc=TemplateAssertionError, 393 ) 394 if self.stream.skip_if("name:as"): 395 alias = self.parse_assign_target(name_only=True) 396 node.names.append((target.name, alias.name)) 397 else: 398 node.names.append(target.name) 399 if parse_context() or self.stream.current.type != "comma": 400 break 401 else: 402 self.stream.expect("name") 403 if not hasattr(node, "with_context"): 404 node.with_context = False 405 return node
407 def parse_signature(self, node: _MacroCall) -> None: 408 args = node.args = [] 409 defaults = node.defaults = [] 410 self.stream.expect("lparen") 411 while self.stream.current.type != "rparen": 412 if args: 413 self.stream.expect("comma") 414 arg = self.parse_assign_target(name_only=True) 415 arg.set_ctx("param") 416 if self.stream.skip_if("assign"): 417 defaults.append(self.parse_expression()) 418 elif defaults: 419 self.fail("non-default argument follows default argument") 420 args.append(arg) 421 self.stream.expect("rparen")
423 def parse_call_block(self) -> nodes.CallBlock: 424 node = nodes.CallBlock(lineno=next(self.stream).lineno) 425 if self.stream.current.type == "lparen": 426 self.parse_signature(node) 427 else: 428 node.args = [] 429 node.defaults = [] 430 431 call_node = self.parse_expression() 432 if not isinstance(call_node, nodes.Call): 433 self.fail("expected call", node.lineno) 434 node.call = call_node 435 node.body = self.parse_statements(("name:endcall",), drop_needle=True) 436 return node
474 def parse_assign_target( 475 self, 476 with_tuple: bool = True, 477 name_only: bool = False, 478 extra_end_rules: t.Optional[t.Tuple[str, ...]] = None, 479 with_namespace: bool = False, 480 ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]: 481 """Parse an assignment target. As Jinja allows assignments to 482 tuples, this function can parse all allowed assignment targets. Per 483 default assignments to tuples are parsed, that can be disable however 484 by setting `with_tuple` to `False`. If only assignments to names are 485 wanted `name_only` can be set to `True`. The `extra_end_rules` 486 parameter is forwarded to the tuple parsing function. If 487 `with_namespace` is enabled, a namespace assignment may be parsed. 488 """ 489 target: nodes.Expr 490 491 if with_namespace and self.stream.look().type == "dot": 492 token = self.stream.expect("name") 493 next(self.stream) # dot 494 attr = self.stream.expect("name") 495 target = nodes.NSRef(token.value, attr.value, lineno=token.lineno) 496 elif name_only: 497 token = self.stream.expect("name") 498 target = nodes.Name(token.value, "store", lineno=token.lineno) 499 else: 500 if with_tuple: 501 target = self.parse_tuple( 502 simplified=True, extra_end_rules=extra_end_rules 503 ) 504 else: 505 target = self.parse_primary() 506 507 target.set_ctx("store") 508 509 if not target.can_assign(): 510 self.fail( 511 f"can't assign to {type(target).__name__.lower()!r}", target.lineno 512 ) 513 514 return target # type: ignore
Parse an assignment target. As Jinja allows assignments to
tuples, this function can parse all allowed assignment targets. Per
default assignments to tuples are parsed, that can be disable however
by setting with_tuple
to False
. If only assignments to names are
wanted name_only
can be set to True
. The extra_end_rules
parameter is forwarded to the tuple parsing function. If
with_namespace
is enabled, a namespace assignment may be parsed.
516 def parse_expression(self, with_condexpr: bool = True) -> nodes.Expr: 517 """Parse an expression. Per default all expressions are parsed, if 518 the optional `with_condexpr` parameter is set to `False` conditional 519 expressions are not parsed. 520 """ 521 if with_condexpr: 522 return self.parse_condexpr() 523 return self.parse_or()
Parse an expression. Per default all expressions are parsed, if
the optional with_condexpr
parameter is set to False
conditional
expressions are not parsed.
525 def parse_condexpr(self) -> nodes.Expr: 526 lineno = self.stream.current.lineno 527 expr1 = self.parse_or() 528 expr3: t.Optional[nodes.Expr] 529 530 while self.stream.skip_if("name:if"): 531 expr2 = self.parse_or() 532 if self.stream.skip_if("name:else"): 533 expr3 = self.parse_condexpr() 534 else: 535 expr3 = None 536 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno) 537 lineno = self.stream.current.lineno 538 return expr1
564 def parse_compare(self) -> nodes.Expr: 565 lineno = self.stream.current.lineno 566 expr = self.parse_math1() 567 ops = [] 568 while True: 569 token_type = self.stream.current.type 570 if token_type in _compare_operators: 571 next(self.stream) 572 ops.append(nodes.Operand(token_type, self.parse_math1())) 573 elif self.stream.skip_if("name:in"): 574 ops.append(nodes.Operand("in", self.parse_math1())) 575 elif self.stream.current.test("name:not") and self.stream.look().test( 576 "name:in" 577 ): 578 self.stream.skip(2) 579 ops.append(nodes.Operand("notin", self.parse_math1())) 580 else: 581 break 582 lineno = self.stream.current.lineno 583 if not ops: 584 return expr 585 return nodes.Compare(expr, ops, lineno=lineno)
587 def parse_math1(self) -> nodes.Expr: 588 lineno = self.stream.current.lineno 589 left = self.parse_concat() 590 while self.stream.current.type in ("add", "sub"): 591 cls = _math_nodes[self.stream.current.type] 592 next(self.stream) 593 right = self.parse_concat() 594 left = cls(left, right, lineno=lineno) 595 lineno = self.stream.current.lineno 596 return left
598 def parse_concat(self) -> nodes.Expr: 599 lineno = self.stream.current.lineno 600 args = [self.parse_math2()] 601 while self.stream.current.type == "tilde": 602 next(self.stream) 603 args.append(self.parse_math2()) 604 if len(args) == 1: 605 return args[0] 606 return nodes.Concat(args, lineno=lineno)
608 def parse_math2(self) -> nodes.Expr: 609 lineno = self.stream.current.lineno 610 left = self.parse_pow() 611 while self.stream.current.type in ("mul", "div", "floordiv", "mod"): 612 cls = _math_nodes[self.stream.current.type] 613 next(self.stream) 614 right = self.parse_pow() 615 left = cls(left, right, lineno=lineno) 616 lineno = self.stream.current.lineno 617 return left
619 def parse_pow(self) -> nodes.Expr: 620 lineno = self.stream.current.lineno 621 left = self.parse_unary() 622 while self.stream.current.type == "pow": 623 next(self.stream) 624 right = self.parse_unary() 625 left = nodes.Pow(left, right, lineno=lineno) 626 lineno = self.stream.current.lineno 627 return left
629 def parse_unary(self, with_filter: bool = True) -> nodes.Expr: 630 token_type = self.stream.current.type 631 lineno = self.stream.current.lineno 632 node: nodes.Expr 633 634 if token_type == "sub": 635 next(self.stream) 636 node = nodes.Neg(self.parse_unary(False), lineno=lineno) 637 elif token_type == "add": 638 next(self.stream) 639 node = nodes.Pos(self.parse_unary(False), lineno=lineno) 640 else: 641 node = self.parse_primary() 642 node = self.parse_postfix(node) 643 if with_filter: 644 node = self.parse_filter_expr(node) 645 return node
647 def parse_primary(self) -> nodes.Expr: 648 token = self.stream.current 649 node: nodes.Expr 650 if token.type == "name": 651 if token.value in ("true", "false", "True", "False"): 652 node = nodes.Const(token.value in ("true", "True"), lineno=token.lineno) 653 elif token.value in ("none", "None"): 654 node = nodes.Const(None, lineno=token.lineno) 655 else: 656 node = nodes.Name(token.value, "load", lineno=token.lineno) 657 next(self.stream) 658 elif token.type == "string": 659 next(self.stream) 660 buf = [token.value] 661 lineno = token.lineno 662 while self.stream.current.type == "string": 663 buf.append(self.stream.current.value) 664 next(self.stream) 665 node = nodes.Const("".join(buf), lineno=lineno) 666 elif token.type in ("integer", "float"): 667 next(self.stream) 668 node = nodes.Const(token.value, lineno=token.lineno) 669 elif token.type == "lparen": 670 next(self.stream) 671 node = self.parse_tuple(explicit_parentheses=True) 672 self.stream.expect("rparen") 673 elif token.type == "lbracket": 674 node = self.parse_list() 675 elif token.type == "lbrace": 676 node = self.parse_dict() 677 else: 678 self.fail(f"unexpected {describe_token(token)!r}", token.lineno) 679 return node
681 def parse_tuple( 682 self, 683 simplified: bool = False, 684 with_condexpr: bool = True, 685 extra_end_rules: t.Optional[t.Tuple[str, ...]] = None, 686 explicit_parentheses: bool = False, 687 ) -> t.Union[nodes.Tuple, nodes.Expr]: 688 """Works like `parse_expression` but if multiple expressions are 689 delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created. 690 This method could also return a regular expression instead of a tuple 691 if no commas where found. 692 693 The default parsing mode is a full tuple. If `simplified` is `True` 694 only names and literals are parsed. The `no_condexpr` parameter is 695 forwarded to :meth:`parse_expression`. 696 697 Because tuples do not require delimiters and may end in a bogus comma 698 an extra hint is needed that marks the end of a tuple. For example 699 for loops support tuples between `for` and `in`. In that case the 700 `extra_end_rules` is set to ``['name:in']``. 701 702 `explicit_parentheses` is true if the parsing was triggered by an 703 expression in parentheses. This is used to figure out if an empty 704 tuple is a valid expression or not. 705 """ 706 lineno = self.stream.current.lineno 707 if simplified: 708 parse = self.parse_primary 709 elif with_condexpr: 710 parse = self.parse_expression 711 else: 712 713 def parse() -> nodes.Expr: 714 return self.parse_expression(with_condexpr=False) 715 716 args: t.List[nodes.Expr] = [] 717 is_tuple = False 718 719 while True: 720 if args: 721 self.stream.expect("comma") 722 if self.is_tuple_end(extra_end_rules): 723 break 724 args.append(parse()) 725 if self.stream.current.type == "comma": 726 is_tuple = True 727 else: 728 break 729 lineno = self.stream.current.lineno 730 731 if not is_tuple: 732 if args: 733 return args[0] 734 735 # if we don't have explicit parentheses, an empty tuple is 736 # not a valid expression. This would mean nothing (literally 737 # nothing) in the spot of an expression would be an empty 738 # tuple. 739 if not explicit_parentheses: 740 self.fail( 741 "Expected an expression," 742 f" got {describe_token(self.stream.current)!r}" 743 ) 744 745 return nodes.Tuple(args, "load", lineno=lineno)
Works like parse_expression
but if multiple expressions are
delimited by a comma a ~jinja2.nodes.Tuple
node is created.
This method could also return a regular expression instead of a tuple
if no commas where found.
The default parsing mode is a full tuple. If simplified
is True
only names and literals are parsed. The no_condexpr
parameter is
forwarded to parse_expression()
.
Because tuples do not require delimiters and may end in a bogus comma
an extra hint is needed that marks the end of a tuple. For example
for loops support tuples between for
and in
. In that case the
extra_end_rules
is set to ['name:in']
.
explicit_parentheses
is true if the parsing was triggered by an
expression in parentheses. This is used to figure out if an empty
tuple is a valid expression or not.
747 def parse_list(self) -> nodes.List: 748 token = self.stream.expect("lbracket") 749 items: t.List[nodes.Expr] = [] 750 while self.stream.current.type != "rbracket": 751 if items: 752 self.stream.expect("comma") 753 if self.stream.current.type == "rbracket": 754 break 755 items.append(self.parse_expression()) 756 self.stream.expect("rbracket") 757 return nodes.List(items, lineno=token.lineno)
759 def parse_dict(self) -> nodes.Dict: 760 token = self.stream.expect("lbrace") 761 items: t.List[nodes.Pair] = [] 762 while self.stream.current.type != "rbrace": 763 if items: 764 self.stream.expect("comma") 765 if self.stream.current.type == "rbrace": 766 break 767 key = self.parse_expression() 768 self.stream.expect("colon") 769 value = self.parse_expression() 770 items.append(nodes.Pair(key, value, lineno=key.lineno)) 771 self.stream.expect("rbrace") 772 return nodes.Dict(items, lineno=token.lineno)
774 def parse_postfix(self, node: nodes.Expr) -> nodes.Expr: 775 while True: 776 token_type = self.stream.current.type 777 if token_type == "dot" or token_type == "lbracket": 778 node = self.parse_subscript(node) 779 # calls are valid both after postfix expressions (getattr 780 # and getitem) as well as filters and tests 781 elif token_type == "lparen": 782 node = self.parse_call(node) 783 else: 784 break 785 return node
787 def parse_filter_expr(self, node: nodes.Expr) -> nodes.Expr: 788 while True: 789 token_type = self.stream.current.type 790 if token_type == "pipe": 791 node = self.parse_filter(node) # type: ignore 792 elif token_type == "name" and self.stream.current.value == "is": 793 node = self.parse_test(node) 794 # calls are valid both after postfix expressions (getattr 795 # and getitem) as well as filters and tests 796 elif token_type == "lparen": 797 node = self.parse_call(node) 798 else: 799 break 800 return node
802 def parse_subscript( 803 self, node: nodes.Expr 804 ) -> t.Union[nodes.Getattr, nodes.Getitem]: 805 token = next(self.stream) 806 arg: nodes.Expr 807 808 if token.type == "dot": 809 attr_token = self.stream.current 810 next(self.stream) 811 if attr_token.type == "name": 812 return nodes.Getattr( 813 node, attr_token.value, "load", lineno=token.lineno 814 ) 815 elif attr_token.type != "integer": 816 self.fail("expected name or number", attr_token.lineno) 817 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno) 818 return nodes.Getitem(node, arg, "load", lineno=token.lineno) 819 if token.type == "lbracket": 820 args: t.List[nodes.Expr] = [] 821 while self.stream.current.type != "rbracket": 822 if args: 823 self.stream.expect("comma") 824 args.append(self.parse_subscribed()) 825 self.stream.expect("rbracket") 826 if len(args) == 1: 827 arg = args[0] 828 else: 829 arg = nodes.Tuple(args, "load", lineno=token.lineno) 830 return nodes.Getitem(node, arg, "load", lineno=token.lineno) 831 self.fail("expected subscript expression", token.lineno)
833 def parse_subscribed(self) -> nodes.Expr: 834 lineno = self.stream.current.lineno 835 args: t.List[t.Optional[nodes.Expr]] 836 837 if self.stream.current.type == "colon": 838 next(self.stream) 839 args = [None] 840 else: 841 node = self.parse_expression() 842 if self.stream.current.type != "colon": 843 return node 844 next(self.stream) 845 args = [node] 846 847 if self.stream.current.type == "colon": 848 args.append(None) 849 elif self.stream.current.type not in ("rbracket", "comma"): 850 args.append(self.parse_expression()) 851 else: 852 args.append(None) 853 854 if self.stream.current.type == "colon": 855 next(self.stream) 856 if self.stream.current.type not in ("rbracket", "comma"): 857 args.append(self.parse_expression()) 858 else: 859 args.append(None) 860 else: 861 args.append(None) 862 863 return nodes.Slice(lineno=lineno, *args) # noqa: B026
865 def parse_call_args( 866 self, 867 ) -> t.Tuple[ 868 t.List[nodes.Expr], 869 t.List[nodes.Keyword], 870 t.Optional[nodes.Expr], 871 t.Optional[nodes.Expr], 872 ]: 873 token = self.stream.expect("lparen") 874 args = [] 875 kwargs = [] 876 dyn_args = None 877 dyn_kwargs = None 878 require_comma = False 879 880 def ensure(expr: bool) -> None: 881 if not expr: 882 self.fail("invalid syntax for function call expression", token.lineno) 883 884 while self.stream.current.type != "rparen": 885 if require_comma: 886 self.stream.expect("comma") 887 888 # support for trailing comma 889 if self.stream.current.type == "rparen": 890 break 891 892 if self.stream.current.type == "mul": 893 ensure(dyn_args is None and dyn_kwargs is None) 894 next(self.stream) 895 dyn_args = self.parse_expression() 896 elif self.stream.current.type == "pow": 897 ensure(dyn_kwargs is None) 898 next(self.stream) 899 dyn_kwargs = self.parse_expression() 900 else: 901 if ( 902 self.stream.current.type == "name" 903 and self.stream.look().type == "assign" 904 ): 905 # Parsing a kwarg 906 ensure(dyn_kwargs is None) 907 key = self.stream.current.value 908 self.stream.skip(2) 909 value = self.parse_expression() 910 kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) 911 else: 912 # Parsing an arg 913 ensure(dyn_args is None and dyn_kwargs is None and not kwargs) 914 args.append(self.parse_expression()) 915 916 require_comma = True 917 918 self.stream.expect("rparen") 919 return args, kwargs, dyn_args, dyn_kwargs
921 def parse_call(self, node: nodes.Expr) -> nodes.Call: 922 # The lparen will be expected in parse_call_args, but the lineno 923 # needs to be recorded before the stream is advanced. 924 token = self.stream.current 925 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 926 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno)
928 def parse_filter( 929 self, node: t.Optional[nodes.Expr], start_inline: bool = False 930 ) -> t.Optional[nodes.Expr]: 931 while self.stream.current.type == "pipe" or start_inline: 932 if not start_inline: 933 next(self.stream) 934 token = self.stream.expect("name") 935 name = token.value 936 while self.stream.current.type == "dot": 937 next(self.stream) 938 name += "." + self.stream.expect("name").value 939 if self.stream.current.type == "lparen": 940 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 941 else: 942 args = [] 943 kwargs = [] 944 dyn_args = dyn_kwargs = None 945 node = nodes.Filter( 946 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 947 ) 948 start_inline = False 949 return node
951 def parse_test(self, node: nodes.Expr) -> nodes.Expr: 952 token = next(self.stream) 953 if self.stream.current.test("name:not"): 954 next(self.stream) 955 negated = True 956 else: 957 negated = False 958 name = self.stream.expect("name").value 959 while self.stream.current.type == "dot": 960 next(self.stream) 961 name += "." + self.stream.expect("name").value 962 dyn_args = dyn_kwargs = None 963 kwargs: t.List[nodes.Keyword] = [] 964 if self.stream.current.type == "lparen": 965 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 966 elif self.stream.current.type in { 967 "name", 968 "string", 969 "integer", 970 "float", 971 "lparen", 972 "lbracket", 973 "lbrace", 974 } and not self.stream.current.test_any("name:else", "name:or", "name:and"): 975 if self.stream.current.test("name:is"): 976 self.fail("You cannot chain multiple tests with is") 977 arg_node = self.parse_primary() 978 arg_node = self.parse_postfix(arg_node) 979 args = [arg_node] 980 else: 981 args = [] 982 node = nodes.Test( 983 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 984 ) 985 if negated: 986 node = nodes.Not(node, lineno=token.lineno) 987 return node
989 def subparse( 990 self, end_tokens: t.Optional[t.Tuple[str, ...]] = None 991 ) -> t.List[nodes.Node]: 992 body: t.List[nodes.Node] = [] 993 data_buffer: t.List[nodes.Node] = [] 994 add_data = data_buffer.append 995 996 if end_tokens is not None: 997 self._end_token_stack.append(end_tokens) 998 999 def flush_data() -> None: 1000 if data_buffer: 1001 lineno = data_buffer[0].lineno 1002 body.append(nodes.Output(data_buffer[:], lineno=lineno)) 1003 del data_buffer[:] 1004 1005 try: 1006 while self.stream: 1007 token = self.stream.current 1008 if token.type == "data": 1009 if token.value: 1010 add_data(nodes.TemplateData(token.value, lineno=token.lineno)) 1011 next(self.stream) 1012 elif token.type == "variable_begin": 1013 next(self.stream) 1014 add_data(self.parse_tuple(with_condexpr=True)) 1015 self.stream.expect("variable_end") 1016 elif token.type == "block_begin": 1017 flush_data() 1018 next(self.stream) 1019 if end_tokens is not None and self.stream.current.test_any( 1020 *end_tokens 1021 ): 1022 return body 1023 rv = self.parse_statement() 1024 if isinstance(rv, list): 1025 body.extend(rv) 1026 else: 1027 body.append(rv) 1028 self.stream.expect("block_end") 1029 else: 1030 raise AssertionError("internal parsing error") 1031 1032 flush_data() 1033 finally: 1034 if end_tokens is not None: 1035 self._end_token_stack.pop() 1036 return body