jinja2.parser
Parse tokens from the lexer into nodes for the compiler.
1"""Parse tokens from the lexer into nodes for the compiler.""" 2import typing 3import typing as t 4 5from . import nodes 6from .exceptions import TemplateAssertionError 7from .exceptions import TemplateSyntaxError 8from .lexer import describe_token 9from .lexer import describe_token_expr 10 11if t.TYPE_CHECKING: 12 import typing_extensions as te 13 from .environment import Environment 14 15_ImportInclude = t.TypeVar("_ImportInclude", nodes.Import, nodes.Include) 16_MacroCall = t.TypeVar("_MacroCall", nodes.Macro, nodes.CallBlock) 17 18_statement_keywords = frozenset( 19 [ 20 "for", 21 "if", 22 "block", 23 "extends", 24 "print", 25 "macro", 26 "include", 27 "from", 28 "import", 29 "set", 30 "with", 31 "autoescape", 32 ] 33) 34_compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"]) 35 36_math_nodes: t.Dict[str, t.Type[nodes.Expr]] = { 37 "add": nodes.Add, 38 "sub": nodes.Sub, 39 "mul": nodes.Mul, 40 "div": nodes.Div, 41 "floordiv": nodes.FloorDiv, 42 "mod": nodes.Mod, 43} 44 45 46class Parser: 47 """This is the central parsing class Jinja uses. It's passed to 48 extensions and can be used to parse expressions or statements. 49 """ 50 51 def __init__( 52 self, 53 environment: "Environment", 54 source: str, 55 name: t.Optional[str] = None, 56 filename: t.Optional[str] = None, 57 state: t.Optional[str] = None, 58 ) -> None: 59 self.environment = environment 60 self.stream = environment._tokenize(source, name, filename, state) 61 self.name = name 62 self.filename = filename 63 self.closed = False 64 self.extensions: t.Dict[ 65 str, t.Callable[["Parser"], t.Union[nodes.Node, t.List[nodes.Node]]] 66 ] = {} 67 for extension in environment.iter_extensions(): 68 for tag in extension.tags: 69 self.extensions[tag] = extension.parse 70 self._last_identifier = 0 71 self._tag_stack: t.List[str] = [] 72 self._end_token_stack: t.List[t.Tuple[str, ...]] = [] 73 74 def fail( 75 self, 76 msg: str, 77 lineno: t.Optional[int] = None, 78 exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError, 79 ) -> "te.NoReturn": 80 """Convenience method that raises `exc` with the message, passed 81 line number or last line number as well as the current name and 82 filename. 83 """ 84 if lineno is None: 85 lineno = self.stream.current.lineno 86 raise exc(msg, lineno, self.name, self.filename) 87 88 def _fail_ut_eof( 89 self, 90 name: t.Optional[str], 91 end_token_stack: t.List[t.Tuple[str, ...]], 92 lineno: t.Optional[int], 93 ) -> "te.NoReturn": 94 expected: t.Set[str] = set() 95 for exprs in end_token_stack: 96 expected.update(map(describe_token_expr, exprs)) 97 if end_token_stack: 98 currently_looking: t.Optional[str] = " or ".join( 99 map(repr, map(describe_token_expr, end_token_stack[-1])) 100 ) 101 else: 102 currently_looking = None 103 104 if name is None: 105 message = ["Unexpected end of template."] 106 else: 107 message = [f"Encountered unknown tag {name!r}."] 108 109 if currently_looking: 110 if name is not None and name in expected: 111 message.append( 112 "You probably made a nesting mistake. Jinja is expecting this tag," 113 f" but currently looking for {currently_looking}." 114 ) 115 else: 116 message.append( 117 f"Jinja was looking for the following tags: {currently_looking}." 118 ) 119 120 if self._tag_stack: 121 message.append( 122 "The innermost block that needs to be closed is" 123 f" {self._tag_stack[-1]!r}." 124 ) 125 126 self.fail(" ".join(message), lineno) 127 128 def fail_unknown_tag( 129 self, name: str, lineno: t.Optional[int] = None 130 ) -> "te.NoReturn": 131 """Called if the parser encounters an unknown tag. Tries to fail 132 with a human readable error message that could help to identify 133 the problem. 134 """ 135 self._fail_ut_eof(name, self._end_token_stack, lineno) 136 137 def fail_eof( 138 self, 139 end_tokens: t.Optional[t.Tuple[str, ...]] = None, 140 lineno: t.Optional[int] = None, 141 ) -> "te.NoReturn": 142 """Like fail_unknown_tag but for end of template situations.""" 143 stack = list(self._end_token_stack) 144 if end_tokens is not None: 145 stack.append(end_tokens) 146 self._fail_ut_eof(None, stack, lineno) 147 148 def is_tuple_end( 149 self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None 150 ) -> bool: 151 """Are we at the end of a tuple?""" 152 if self.stream.current.type in ("variable_end", "block_end", "rparen"): 153 return True 154 elif extra_end_rules is not None: 155 return self.stream.current.test_any(extra_end_rules) # type: ignore 156 return False 157 158 def free_identifier(self, lineno: t.Optional[int] = None) -> nodes.InternalName: 159 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" 160 self._last_identifier += 1 161 rv = object.__new__(nodes.InternalName) 162 nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno) 163 return rv 164 165 def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]: 166 """Parse a single statement.""" 167 token = self.stream.current 168 if token.type != "name": 169 self.fail("tag name expected", token.lineno) 170 self._tag_stack.append(token.value) 171 pop_tag = True 172 try: 173 if token.value in _statement_keywords: 174 f = getattr(self, f"parse_{self.stream.current.value}") 175 return f() # type: ignore 176 if token.value == "call": 177 return self.parse_call_block() 178 if token.value == "filter": 179 return self.parse_filter_block() 180 ext = self.extensions.get(token.value) 181 if ext is not None: 182 return ext(self) 183 184 # did not work out, remove the token we pushed by accident 185 # from the stack so that the unknown tag fail function can 186 # produce a proper error message. 187 self._tag_stack.pop() 188 pop_tag = False 189 self.fail_unknown_tag(token.value, token.lineno) 190 finally: 191 if pop_tag: 192 self._tag_stack.pop() 193 194 def parse_statements( 195 self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False 196 ) -> t.List[nodes.Node]: 197 """Parse multiple statements into a list until one of the end tokens 198 is reached. This is used to parse the body of statements as it also 199 parses template data if appropriate. The parser checks first if the 200 current token is a colon and skips it if there is one. Then it checks 201 for the block end and parses until if one of the `end_tokens` is 202 reached. Per default the active token in the stream at the end of 203 the call is the matched end token. If this is not wanted `drop_needle` 204 can be set to `True` and the end token is removed. 205 """ 206 # the first token may be a colon for python compatibility 207 self.stream.skip_if("colon") 208 209 # in the future it would be possible to add whole code sections 210 # by adding some sort of end of statement token and parsing those here. 211 self.stream.expect("block_end") 212 result = self.subparse(end_tokens) 213 214 # we reached the end of the template too early, the subparser 215 # does not check for this, so we do that now 216 if self.stream.current.type == "eof": 217 self.fail_eof(end_tokens) 218 219 if drop_needle: 220 next(self.stream) 221 return result 222 223 def parse_set(self) -> t.Union[nodes.Assign, nodes.AssignBlock]: 224 """Parse an assign statement.""" 225 lineno = next(self.stream).lineno 226 target = self.parse_assign_target(with_namespace=True) 227 if self.stream.skip_if("assign"): 228 expr = self.parse_tuple() 229 return nodes.Assign(target, expr, lineno=lineno) 230 filter_node = self.parse_filter(None) 231 body = self.parse_statements(("name:endset",), drop_needle=True) 232 return nodes.AssignBlock(target, filter_node, body, lineno=lineno) 233 234 def parse_for(self) -> nodes.For: 235 """Parse a for loop.""" 236 lineno = self.stream.expect("name:for").lineno 237 target = self.parse_assign_target(extra_end_rules=("name:in",)) 238 self.stream.expect("name:in") 239 iter = self.parse_tuple( 240 with_condexpr=False, extra_end_rules=("name:recursive",) 241 ) 242 test = None 243 if self.stream.skip_if("name:if"): 244 test = self.parse_expression() 245 recursive = self.stream.skip_if("name:recursive") 246 body = self.parse_statements(("name:endfor", "name:else")) 247 if next(self.stream).value == "endfor": 248 else_ = [] 249 else: 250 else_ = self.parse_statements(("name:endfor",), drop_needle=True) 251 return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno) 252 253 def parse_if(self) -> nodes.If: 254 """Parse an if construct.""" 255 node = result = nodes.If(lineno=self.stream.expect("name:if").lineno) 256 while True: 257 node.test = self.parse_tuple(with_condexpr=False) 258 node.body = self.parse_statements(("name:elif", "name:else", "name:endif")) 259 node.elif_ = [] 260 node.else_ = [] 261 token = next(self.stream) 262 if token.test("name:elif"): 263 node = nodes.If(lineno=self.stream.current.lineno) 264 result.elif_.append(node) 265 continue 266 elif token.test("name:else"): 267 result.else_ = self.parse_statements(("name:endif",), drop_needle=True) 268 break 269 return result 270 271 def parse_with(self) -> nodes.With: 272 node = nodes.With(lineno=next(self.stream).lineno) 273 targets: t.List[nodes.Expr] = [] 274 values: t.List[nodes.Expr] = [] 275 while self.stream.current.type != "block_end": 276 if targets: 277 self.stream.expect("comma") 278 target = self.parse_assign_target() 279 target.set_ctx("param") 280 targets.append(target) 281 self.stream.expect("assign") 282 values.append(self.parse_expression()) 283 node.targets = targets 284 node.values = values 285 node.body = self.parse_statements(("name:endwith",), drop_needle=True) 286 return node 287 288 def parse_autoescape(self) -> nodes.Scope: 289 node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno) 290 node.options = [nodes.Keyword("autoescape", self.parse_expression())] 291 node.body = self.parse_statements(("name:endautoescape",), drop_needle=True) 292 return nodes.Scope([node]) 293 294 def parse_block(self) -> nodes.Block: 295 node = nodes.Block(lineno=next(self.stream).lineno) 296 node.name = self.stream.expect("name").value 297 node.scoped = self.stream.skip_if("name:scoped") 298 node.required = self.stream.skip_if("name:required") 299 300 # common problem people encounter when switching from django 301 # to jinja. we do not support hyphens in block names, so let's 302 # raise a nicer error message in that case. 303 if self.stream.current.type == "sub": 304 self.fail( 305 "Block names in Jinja have to be valid Python identifiers and may not" 306 " contain hyphens, use an underscore instead." 307 ) 308 309 node.body = self.parse_statements(("name:endblock",), drop_needle=True) 310 311 # enforce that required blocks only contain whitespace or comments 312 # by asserting that the body, if not empty, is just TemplateData nodes 313 # with whitespace data 314 if node.required: 315 for body_node in node.body: 316 if not isinstance(body_node, nodes.Output) or any( 317 not isinstance(output_node, nodes.TemplateData) 318 or not output_node.data.isspace() 319 for output_node in body_node.nodes 320 ): 321 self.fail("Required blocks can only contain comments or whitespace") 322 323 self.stream.skip_if("name:" + node.name) 324 return node 325 326 def parse_extends(self) -> nodes.Extends: 327 node = nodes.Extends(lineno=next(self.stream).lineno) 328 node.template = self.parse_expression() 329 return node 330 331 def parse_import_context( 332 self, node: _ImportInclude, default: bool 333 ) -> _ImportInclude: 334 if self.stream.current.test_any( 335 "name:with", "name:without" 336 ) and self.stream.look().test("name:context"): 337 node.with_context = next(self.stream).value == "with" 338 self.stream.skip() 339 else: 340 node.with_context = default 341 return node 342 343 def parse_include(self) -> nodes.Include: 344 node = nodes.Include(lineno=next(self.stream).lineno) 345 node.template = self.parse_expression() 346 if self.stream.current.test("name:ignore") and self.stream.look().test( 347 "name:missing" 348 ): 349 node.ignore_missing = True 350 self.stream.skip(2) 351 else: 352 node.ignore_missing = False 353 return self.parse_import_context(node, True) 354 355 def parse_import(self) -> nodes.Import: 356 node = nodes.Import(lineno=next(self.stream).lineno) 357 node.template = self.parse_expression() 358 self.stream.expect("name:as") 359 node.target = self.parse_assign_target(name_only=True).name 360 return self.parse_import_context(node, False) 361 362 def parse_from(self) -> nodes.FromImport: 363 node = nodes.FromImport(lineno=next(self.stream).lineno) 364 node.template = self.parse_expression() 365 self.stream.expect("name:import") 366 node.names = [] 367 368 def parse_context() -> bool: 369 if self.stream.current.value in { 370 "with", 371 "without", 372 } and self.stream.look().test("name:context"): 373 node.with_context = next(self.stream).value == "with" 374 self.stream.skip() 375 return True 376 return False 377 378 while True: 379 if node.names: 380 self.stream.expect("comma") 381 if self.stream.current.type == "name": 382 if parse_context(): 383 break 384 target = self.parse_assign_target(name_only=True) 385 if target.name.startswith("_"): 386 self.fail( 387 "names starting with an underline can not be imported", 388 target.lineno, 389 exc=TemplateAssertionError, 390 ) 391 if self.stream.skip_if("name:as"): 392 alias = self.parse_assign_target(name_only=True) 393 node.names.append((target.name, alias.name)) 394 else: 395 node.names.append(target.name) 396 if parse_context() or self.stream.current.type != "comma": 397 break 398 else: 399 self.stream.expect("name") 400 if not hasattr(node, "with_context"): 401 node.with_context = False 402 return node 403 404 def parse_signature(self, node: _MacroCall) -> None: 405 args = node.args = [] 406 defaults = node.defaults = [] 407 self.stream.expect("lparen") 408 while self.stream.current.type != "rparen": 409 if args: 410 self.stream.expect("comma") 411 arg = self.parse_assign_target(name_only=True) 412 arg.set_ctx("param") 413 if self.stream.skip_if("assign"): 414 defaults.append(self.parse_expression()) 415 elif defaults: 416 self.fail("non-default argument follows default argument") 417 args.append(arg) 418 self.stream.expect("rparen") 419 420 def parse_call_block(self) -> nodes.CallBlock: 421 node = nodes.CallBlock(lineno=next(self.stream).lineno) 422 if self.stream.current.type == "lparen": 423 self.parse_signature(node) 424 else: 425 node.args = [] 426 node.defaults = [] 427 428 call_node = self.parse_expression() 429 if not isinstance(call_node, nodes.Call): 430 self.fail("expected call", node.lineno) 431 node.call = call_node 432 node.body = self.parse_statements(("name:endcall",), drop_needle=True) 433 return node 434 435 def parse_filter_block(self) -> nodes.FilterBlock: 436 node = nodes.FilterBlock(lineno=next(self.stream).lineno) 437 node.filter = self.parse_filter(None, start_inline=True) # type: ignore 438 node.body = self.parse_statements(("name:endfilter",), drop_needle=True) 439 return node 440 441 def parse_macro(self) -> nodes.Macro: 442 node = nodes.Macro(lineno=next(self.stream).lineno) 443 node.name = self.parse_assign_target(name_only=True).name 444 self.parse_signature(node) 445 node.body = self.parse_statements(("name:endmacro",), drop_needle=True) 446 return node 447 448 def parse_print(self) -> nodes.Output: 449 node = nodes.Output(lineno=next(self.stream).lineno) 450 node.nodes = [] 451 while self.stream.current.type != "block_end": 452 if node.nodes: 453 self.stream.expect("comma") 454 node.nodes.append(self.parse_expression()) 455 return node 456 457 @typing.overload 458 def parse_assign_target( 459 self, with_tuple: bool = ..., name_only: "te.Literal[True]" = ... 460 ) -> nodes.Name: 461 ... 462 463 @typing.overload 464 def parse_assign_target( 465 self, 466 with_tuple: bool = True, 467 name_only: bool = False, 468 extra_end_rules: t.Optional[t.Tuple[str, ...]] = None, 469 with_namespace: bool = False, 470 ) -> t.Union[nodes.NSRef, nodes.Name, nodes.Tuple]: 471 ... 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(self) -> t.Tuple: 865 token = self.stream.expect("lparen") 866 args = [] 867 kwargs = [] 868 dyn_args = None 869 dyn_kwargs = None 870 require_comma = False 871 872 def ensure(expr: bool) -> None: 873 if not expr: 874 self.fail("invalid syntax for function call expression", token.lineno) 875 876 while self.stream.current.type != "rparen": 877 if require_comma: 878 self.stream.expect("comma") 879 880 # support for trailing comma 881 if self.stream.current.type == "rparen": 882 break 883 884 if self.stream.current.type == "mul": 885 ensure(dyn_args is None and dyn_kwargs is None) 886 next(self.stream) 887 dyn_args = self.parse_expression() 888 elif self.stream.current.type == "pow": 889 ensure(dyn_kwargs is None) 890 next(self.stream) 891 dyn_kwargs = self.parse_expression() 892 else: 893 if ( 894 self.stream.current.type == "name" 895 and self.stream.look().type == "assign" 896 ): 897 # Parsing a kwarg 898 ensure(dyn_kwargs is None) 899 key = self.stream.current.value 900 self.stream.skip(2) 901 value = self.parse_expression() 902 kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) 903 else: 904 # Parsing an arg 905 ensure(dyn_args is None and dyn_kwargs is None and not kwargs) 906 args.append(self.parse_expression()) 907 908 require_comma = True 909 910 self.stream.expect("rparen") 911 return args, kwargs, dyn_args, dyn_kwargs 912 913 def parse_call(self, node: nodes.Expr) -> nodes.Call: 914 # The lparen will be expected in parse_call_args, but the lineno 915 # needs to be recorded before the stream is advanced. 916 token = self.stream.current 917 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 918 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno) 919 920 def parse_filter( 921 self, node: t.Optional[nodes.Expr], start_inline: bool = False 922 ) -> t.Optional[nodes.Expr]: 923 while self.stream.current.type == "pipe" or start_inline: 924 if not start_inline: 925 next(self.stream) 926 token = self.stream.expect("name") 927 name = token.value 928 while self.stream.current.type == "dot": 929 next(self.stream) 930 name += "." + self.stream.expect("name").value 931 if self.stream.current.type == "lparen": 932 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 933 else: 934 args = [] 935 kwargs = [] 936 dyn_args = dyn_kwargs = None 937 node = nodes.Filter( 938 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 939 ) 940 start_inline = False 941 return node 942 943 def parse_test(self, node: nodes.Expr) -> nodes.Expr: 944 token = next(self.stream) 945 if self.stream.current.test("name:not"): 946 next(self.stream) 947 negated = True 948 else: 949 negated = False 950 name = self.stream.expect("name").value 951 while self.stream.current.type == "dot": 952 next(self.stream) 953 name += "." + self.stream.expect("name").value 954 dyn_args = dyn_kwargs = None 955 kwargs = [] 956 if self.stream.current.type == "lparen": 957 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 958 elif self.stream.current.type in { 959 "name", 960 "string", 961 "integer", 962 "float", 963 "lparen", 964 "lbracket", 965 "lbrace", 966 } and not self.stream.current.test_any("name:else", "name:or", "name:and"): 967 if self.stream.current.test("name:is"): 968 self.fail("You cannot chain multiple tests with is") 969 arg_node = self.parse_primary() 970 arg_node = self.parse_postfix(arg_node) 971 args = [arg_node] 972 else: 973 args = [] 974 node = nodes.Test( 975 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 976 ) 977 if negated: 978 node = nodes.Not(node, lineno=token.lineno) 979 return node 980 981 def subparse( 982 self, end_tokens: t.Optional[t.Tuple[str, ...]] = None 983 ) -> t.List[nodes.Node]: 984 body: t.List[nodes.Node] = [] 985 data_buffer: t.List[nodes.Node] = [] 986 add_data = data_buffer.append 987 988 if end_tokens is not None: 989 self._end_token_stack.append(end_tokens) 990 991 def flush_data() -> None: 992 if data_buffer: 993 lineno = data_buffer[0].lineno 994 body.append(nodes.Output(data_buffer[:], lineno=lineno)) 995 del data_buffer[:] 996 997 try: 998 while self.stream: 999 token = self.stream.current 1000 if token.type == "data": 1001 if token.value: 1002 add_data(nodes.TemplateData(token.value, lineno=token.lineno)) 1003 next(self.stream) 1004 elif token.type == "variable_begin": 1005 next(self.stream) 1006 add_data(self.parse_tuple(with_condexpr=True)) 1007 self.stream.expect("variable_end") 1008 elif token.type == "block_begin": 1009 flush_data() 1010 next(self.stream) 1011 if end_tokens is not None and self.stream.current.test_any( 1012 *end_tokens 1013 ): 1014 return body 1015 rv = self.parse_statement() 1016 if isinstance(rv, list): 1017 body.extend(rv) 1018 else: 1019 body.append(rv) 1020 self.stream.expect("block_end") 1021 else: 1022 raise AssertionError("internal parsing error") 1023 1024 flush_data() 1025 finally: 1026 if end_tokens is not None: 1027 self._end_token_stack.pop() 1028 return body 1029 1030 def parse(self) -> nodes.Template: 1031 """Parse the whole template into a `Template` node.""" 1032 result = nodes.Template(self.subparse(), lineno=1) 1033 result.set_environment(self.environment) 1034 return result
47class Parser: 48 """This is the central parsing class Jinja uses. It's passed to 49 extensions and can be used to parse expressions or statements. 50 """ 51 52 def __init__( 53 self, 54 environment: "Environment", 55 source: str, 56 name: t.Optional[str] = None, 57 filename: t.Optional[str] = None, 58 state: t.Optional[str] = None, 59 ) -> None: 60 self.environment = environment 61 self.stream = environment._tokenize(source, name, filename, state) 62 self.name = name 63 self.filename = filename 64 self.closed = False 65 self.extensions: t.Dict[ 66 str, t.Callable[["Parser"], t.Union[nodes.Node, t.List[nodes.Node]]] 67 ] = {} 68 for extension in environment.iter_extensions(): 69 for tag in extension.tags: 70 self.extensions[tag] = extension.parse 71 self._last_identifier = 0 72 self._tag_stack: t.List[str] = [] 73 self._end_token_stack: t.List[t.Tuple[str, ...]] = [] 74 75 def fail( 76 self, 77 msg: str, 78 lineno: t.Optional[int] = None, 79 exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError, 80 ) -> "te.NoReturn": 81 """Convenience method that raises `exc` with the message, passed 82 line number or last line number as well as the current name and 83 filename. 84 """ 85 if lineno is None: 86 lineno = self.stream.current.lineno 87 raise exc(msg, lineno, self.name, self.filename) 88 89 def _fail_ut_eof( 90 self, 91 name: t.Optional[str], 92 end_token_stack: t.List[t.Tuple[str, ...]], 93 lineno: t.Optional[int], 94 ) -> "te.NoReturn": 95 expected: t.Set[str] = set() 96 for exprs in end_token_stack: 97 expected.update(map(describe_token_expr, exprs)) 98 if end_token_stack: 99 currently_looking: t.Optional[str] = " or ".join( 100 map(repr, map(describe_token_expr, end_token_stack[-1])) 101 ) 102 else: 103 currently_looking = None 104 105 if name is None: 106 message = ["Unexpected end of template."] 107 else: 108 message = [f"Encountered unknown tag {name!r}."] 109 110 if currently_looking: 111 if name is not None and name in expected: 112 message.append( 113 "You probably made a nesting mistake. Jinja is expecting this tag," 114 f" but currently looking for {currently_looking}." 115 ) 116 else: 117 message.append( 118 f"Jinja was looking for the following tags: {currently_looking}." 119 ) 120 121 if self._tag_stack: 122 message.append( 123 "The innermost block that needs to be closed is" 124 f" {self._tag_stack[-1]!r}." 125 ) 126 127 self.fail(" ".join(message), lineno) 128 129 def fail_unknown_tag( 130 self, name: str, lineno: t.Optional[int] = None 131 ) -> "te.NoReturn": 132 """Called if the parser encounters an unknown tag. Tries to fail 133 with a human readable error message that could help to identify 134 the problem. 135 """ 136 self._fail_ut_eof(name, self._end_token_stack, lineno) 137 138 def fail_eof( 139 self, 140 end_tokens: t.Optional[t.Tuple[str, ...]] = None, 141 lineno: t.Optional[int] = None, 142 ) -> "te.NoReturn": 143 """Like fail_unknown_tag but for end of template situations.""" 144 stack = list(self._end_token_stack) 145 if end_tokens is not None: 146 stack.append(end_tokens) 147 self._fail_ut_eof(None, stack, lineno) 148 149 def is_tuple_end( 150 self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None 151 ) -> bool: 152 """Are we at the end of a tuple?""" 153 if self.stream.current.type in ("variable_end", "block_end", "rparen"): 154 return True 155 elif extra_end_rules is not None: 156 return self.stream.current.test_any(extra_end_rules) # type: ignore 157 return False 158 159 def free_identifier(self, lineno: t.Optional[int] = None) -> nodes.InternalName: 160 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" 161 self._last_identifier += 1 162 rv = object.__new__(nodes.InternalName) 163 nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno) 164 return rv 165 166 def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]: 167 """Parse a single statement.""" 168 token = self.stream.current 169 if token.type != "name": 170 self.fail("tag name expected", token.lineno) 171 self._tag_stack.append(token.value) 172 pop_tag = True 173 try: 174 if token.value in _statement_keywords: 175 f = getattr(self, f"parse_{self.stream.current.value}") 176 return f() # type: ignore 177 if token.value == "call": 178 return self.parse_call_block() 179 if token.value == "filter": 180 return self.parse_filter_block() 181 ext = self.extensions.get(token.value) 182 if ext is not None: 183 return ext(self) 184 185 # did not work out, remove the token we pushed by accident 186 # from the stack so that the unknown tag fail function can 187 # produce a proper error message. 188 self._tag_stack.pop() 189 pop_tag = False 190 self.fail_unknown_tag(token.value, token.lineno) 191 finally: 192 if pop_tag: 193 self._tag_stack.pop() 194 195 def parse_statements( 196 self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False 197 ) -> t.List[nodes.Node]: 198 """Parse multiple statements into a list until one of the end tokens 199 is reached. This is used to parse the body of statements as it also 200 parses template data if appropriate. The parser checks first if the 201 current token is a colon and skips it if there is one. Then it checks 202 for the block end and parses until if one of the `end_tokens` is 203 reached. Per default the active token in the stream at the end of 204 the call is the matched end token. If this is not wanted `drop_needle` 205 can be set to `True` and the end token is removed. 206 """ 207 # the first token may be a colon for python compatibility 208 self.stream.skip_if("colon") 209 210 # in the future it would be possible to add whole code sections 211 # by adding some sort of end of statement token and parsing those here. 212 self.stream.expect("block_end") 213 result = self.subparse(end_tokens) 214 215 # we reached the end of the template too early, the subparser 216 # does not check for this, so we do that now 217 if self.stream.current.type == "eof": 218 self.fail_eof(end_tokens) 219 220 if drop_needle: 221 next(self.stream) 222 return result 223 224 def parse_set(self) -> t.Union[nodes.Assign, nodes.AssignBlock]: 225 """Parse an assign statement.""" 226 lineno = next(self.stream).lineno 227 target = self.parse_assign_target(with_namespace=True) 228 if self.stream.skip_if("assign"): 229 expr = self.parse_tuple() 230 return nodes.Assign(target, expr, lineno=lineno) 231 filter_node = self.parse_filter(None) 232 body = self.parse_statements(("name:endset",), drop_needle=True) 233 return nodes.AssignBlock(target, filter_node, body, lineno=lineno) 234 235 def parse_for(self) -> nodes.For: 236 """Parse a for loop.""" 237 lineno = self.stream.expect("name:for").lineno 238 target = self.parse_assign_target(extra_end_rules=("name:in",)) 239 self.stream.expect("name:in") 240 iter = self.parse_tuple( 241 with_condexpr=False, extra_end_rules=("name:recursive",) 242 ) 243 test = None 244 if self.stream.skip_if("name:if"): 245 test = self.parse_expression() 246 recursive = self.stream.skip_if("name:recursive") 247 body = self.parse_statements(("name:endfor", "name:else")) 248 if next(self.stream).value == "endfor": 249 else_ = [] 250 else: 251 else_ = self.parse_statements(("name:endfor",), drop_needle=True) 252 return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno) 253 254 def parse_if(self) -> nodes.If: 255 """Parse an if construct.""" 256 node = result = nodes.If(lineno=self.stream.expect("name:if").lineno) 257 while True: 258 node.test = self.parse_tuple(with_condexpr=False) 259 node.body = self.parse_statements(("name:elif", "name:else", "name:endif")) 260 node.elif_ = [] 261 node.else_ = [] 262 token = next(self.stream) 263 if token.test("name:elif"): 264 node = nodes.If(lineno=self.stream.current.lineno) 265 result.elif_.append(node) 266 continue 267 elif token.test("name:else"): 268 result.else_ = self.parse_statements(("name:endif",), drop_needle=True) 269 break 270 return result 271 272 def parse_with(self) -> nodes.With: 273 node = nodes.With(lineno=next(self.stream).lineno) 274 targets: t.List[nodes.Expr] = [] 275 values: t.List[nodes.Expr] = [] 276 while self.stream.current.type != "block_end": 277 if targets: 278 self.stream.expect("comma") 279 target = self.parse_assign_target() 280 target.set_ctx("param") 281 targets.append(target) 282 self.stream.expect("assign") 283 values.append(self.parse_expression()) 284 node.targets = targets 285 node.values = values 286 node.body = self.parse_statements(("name:endwith",), drop_needle=True) 287 return node 288 289 def parse_autoescape(self) -> nodes.Scope: 290 node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno) 291 node.options = [nodes.Keyword("autoescape", self.parse_expression())] 292 node.body = self.parse_statements(("name:endautoescape",), drop_needle=True) 293 return nodes.Scope([node]) 294 295 def parse_block(self) -> nodes.Block: 296 node = nodes.Block(lineno=next(self.stream).lineno) 297 node.name = self.stream.expect("name").value 298 node.scoped = self.stream.skip_if("name:scoped") 299 node.required = self.stream.skip_if("name:required") 300 301 # common problem people encounter when switching from django 302 # to jinja. we do not support hyphens in block names, so let's 303 # raise a nicer error message in that case. 304 if self.stream.current.type == "sub": 305 self.fail( 306 "Block names in Jinja have to be valid Python identifiers and may not" 307 " contain hyphens, use an underscore instead." 308 ) 309 310 node.body = self.parse_statements(("name:endblock",), drop_needle=True) 311 312 # enforce that required blocks only contain whitespace or comments 313 # by asserting that the body, if not empty, is just TemplateData nodes 314 # with whitespace data 315 if node.required: 316 for body_node in node.body: 317 if not isinstance(body_node, nodes.Output) or any( 318 not isinstance(output_node, nodes.TemplateData) 319 or not output_node.data.isspace() 320 for output_node in body_node.nodes 321 ): 322 self.fail("Required blocks can only contain comments or whitespace") 323 324 self.stream.skip_if("name:" + node.name) 325 return node 326 327 def parse_extends(self) -> nodes.Extends: 328 node = nodes.Extends(lineno=next(self.stream).lineno) 329 node.template = self.parse_expression() 330 return node 331 332 def parse_import_context( 333 self, node: _ImportInclude, default: bool 334 ) -> _ImportInclude: 335 if self.stream.current.test_any( 336 "name:with", "name:without" 337 ) and self.stream.look().test("name:context"): 338 node.with_context = next(self.stream).value == "with" 339 self.stream.skip() 340 else: 341 node.with_context = default 342 return node 343 344 def parse_include(self) -> nodes.Include: 345 node = nodes.Include(lineno=next(self.stream).lineno) 346 node.template = self.parse_expression() 347 if self.stream.current.test("name:ignore") and self.stream.look().test( 348 "name:missing" 349 ): 350 node.ignore_missing = True 351 self.stream.skip(2) 352 else: 353 node.ignore_missing = False 354 return self.parse_import_context(node, True) 355 356 def parse_import(self) -> nodes.Import: 357 node = nodes.Import(lineno=next(self.stream).lineno) 358 node.template = self.parse_expression() 359 self.stream.expect("name:as") 360 node.target = self.parse_assign_target(name_only=True).name 361 return self.parse_import_context(node, False) 362 363 def parse_from(self) -> nodes.FromImport: 364 node = nodes.FromImport(lineno=next(self.stream).lineno) 365 node.template = self.parse_expression() 366 self.stream.expect("name:import") 367 node.names = [] 368 369 def parse_context() -> bool: 370 if self.stream.current.value in { 371 "with", 372 "without", 373 } and self.stream.look().test("name:context"): 374 node.with_context = next(self.stream).value == "with" 375 self.stream.skip() 376 return True 377 return False 378 379 while True: 380 if node.names: 381 self.stream.expect("comma") 382 if self.stream.current.type == "name": 383 if parse_context(): 384 break 385 target = self.parse_assign_target(name_only=True) 386 if target.name.startswith("_"): 387 self.fail( 388 "names starting with an underline can not be imported", 389 target.lineno, 390 exc=TemplateAssertionError, 391 ) 392 if self.stream.skip_if("name:as"): 393 alias = self.parse_assign_target(name_only=True) 394 node.names.append((target.name, alias.name)) 395 else: 396 node.names.append(target.name) 397 if parse_context() or self.stream.current.type != "comma": 398 break 399 else: 400 self.stream.expect("name") 401 if not hasattr(node, "with_context"): 402 node.with_context = False 403 return node 404 405 def parse_signature(self, node: _MacroCall) -> None: 406 args = node.args = [] 407 defaults = node.defaults = [] 408 self.stream.expect("lparen") 409 while self.stream.current.type != "rparen": 410 if args: 411 self.stream.expect("comma") 412 arg = self.parse_assign_target(name_only=True) 413 arg.set_ctx("param") 414 if self.stream.skip_if("assign"): 415 defaults.append(self.parse_expression()) 416 elif defaults: 417 self.fail("non-default argument follows default argument") 418 args.append(arg) 419 self.stream.expect("rparen") 420 421 def parse_call_block(self) -> nodes.CallBlock: 422 node = nodes.CallBlock(lineno=next(self.stream).lineno) 423 if self.stream.current.type == "lparen": 424 self.parse_signature(node) 425 else: 426 node.args = [] 427 node.defaults = [] 428 429 call_node = self.parse_expression() 430 if not isinstance(call_node, nodes.Call): 431 self.fail("expected call", node.lineno) 432 node.call = call_node 433 node.body = self.parse_statements(("name:endcall",), drop_needle=True) 434 return node 435 436 def parse_filter_block(self) -> nodes.FilterBlock: 437 node = nodes.FilterBlock(lineno=next(self.stream).lineno) 438 node.filter = self.parse_filter(None, start_inline=True) # type: ignore 439 node.body = self.parse_statements(("name:endfilter",), drop_needle=True) 440 return node 441 442 def parse_macro(self) -> nodes.Macro: 443 node = nodes.Macro(lineno=next(self.stream).lineno) 444 node.name = self.parse_assign_target(name_only=True).name 445 self.parse_signature(node) 446 node.body = self.parse_statements(("name:endmacro",), drop_needle=True) 447 return node 448 449 def parse_print(self) -> nodes.Output: 450 node = nodes.Output(lineno=next(self.stream).lineno) 451 node.nodes = [] 452 while self.stream.current.type != "block_end": 453 if node.nodes: 454 self.stream.expect("comma") 455 node.nodes.append(self.parse_expression()) 456 return node 457 458 @typing.overload 459 def parse_assign_target( 460 self, with_tuple: bool = ..., name_only: "te.Literal[True]" = ... 461 ) -> nodes.Name: 462 ... 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 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(self) -> t.Tuple: 866 token = self.stream.expect("lparen") 867 args = [] 868 kwargs = [] 869 dyn_args = None 870 dyn_kwargs = None 871 require_comma = False 872 873 def ensure(expr: bool) -> None: 874 if not expr: 875 self.fail("invalid syntax for function call expression", token.lineno) 876 877 while self.stream.current.type != "rparen": 878 if require_comma: 879 self.stream.expect("comma") 880 881 # support for trailing comma 882 if self.stream.current.type == "rparen": 883 break 884 885 if self.stream.current.type == "mul": 886 ensure(dyn_args is None and dyn_kwargs is None) 887 next(self.stream) 888 dyn_args = self.parse_expression() 889 elif self.stream.current.type == "pow": 890 ensure(dyn_kwargs is None) 891 next(self.stream) 892 dyn_kwargs = self.parse_expression() 893 else: 894 if ( 895 self.stream.current.type == "name" 896 and self.stream.look().type == "assign" 897 ): 898 # Parsing a kwarg 899 ensure(dyn_kwargs is None) 900 key = self.stream.current.value 901 self.stream.skip(2) 902 value = self.parse_expression() 903 kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) 904 else: 905 # Parsing an arg 906 ensure(dyn_args is None and dyn_kwargs is None and not kwargs) 907 args.append(self.parse_expression()) 908 909 require_comma = True 910 911 self.stream.expect("rparen") 912 return args, kwargs, dyn_args, dyn_kwargs 913 914 def parse_call(self, node: nodes.Expr) -> nodes.Call: 915 # The lparen will be expected in parse_call_args, but the lineno 916 # needs to be recorded before the stream is advanced. 917 token = self.stream.current 918 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 919 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno) 920 921 def parse_filter( 922 self, node: t.Optional[nodes.Expr], start_inline: bool = False 923 ) -> t.Optional[nodes.Expr]: 924 while self.stream.current.type == "pipe" or start_inline: 925 if not start_inline: 926 next(self.stream) 927 token = self.stream.expect("name") 928 name = token.value 929 while self.stream.current.type == "dot": 930 next(self.stream) 931 name += "." + self.stream.expect("name").value 932 if self.stream.current.type == "lparen": 933 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 934 else: 935 args = [] 936 kwargs = [] 937 dyn_args = dyn_kwargs = None 938 node = nodes.Filter( 939 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 940 ) 941 start_inline = False 942 return node 943 944 def parse_test(self, node: nodes.Expr) -> nodes.Expr: 945 token = next(self.stream) 946 if self.stream.current.test("name:not"): 947 next(self.stream) 948 negated = True 949 else: 950 negated = False 951 name = self.stream.expect("name").value 952 while self.stream.current.type == "dot": 953 next(self.stream) 954 name += "." + self.stream.expect("name").value 955 dyn_args = dyn_kwargs = None 956 kwargs = [] 957 if self.stream.current.type == "lparen": 958 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 959 elif self.stream.current.type in { 960 "name", 961 "string", 962 "integer", 963 "float", 964 "lparen", 965 "lbracket", 966 "lbrace", 967 } and not self.stream.current.test_any("name:else", "name:or", "name:and"): 968 if self.stream.current.test("name:is"): 969 self.fail("You cannot chain multiple tests with is") 970 arg_node = self.parse_primary() 971 arg_node = self.parse_postfix(arg_node) 972 args = [arg_node] 973 else: 974 args = [] 975 node = nodes.Test( 976 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 977 ) 978 if negated: 979 node = nodes.Not(node, lineno=token.lineno) 980 return node 981 982 def subparse( 983 self, end_tokens: t.Optional[t.Tuple[str, ...]] = None 984 ) -> t.List[nodes.Node]: 985 body: t.List[nodes.Node] = [] 986 data_buffer: t.List[nodes.Node] = [] 987 add_data = data_buffer.append 988 989 if end_tokens is not None: 990 self._end_token_stack.append(end_tokens) 991 992 def flush_data() -> None: 993 if data_buffer: 994 lineno = data_buffer[0].lineno 995 body.append(nodes.Output(data_buffer[:], lineno=lineno)) 996 del data_buffer[:] 997 998 try: 999 while self.stream: 1000 token = self.stream.current 1001 if token.type == "data": 1002 if token.value: 1003 add_data(nodes.TemplateData(token.value, lineno=token.lineno)) 1004 next(self.stream) 1005 elif token.type == "variable_begin": 1006 next(self.stream) 1007 add_data(self.parse_tuple(with_condexpr=True)) 1008 self.stream.expect("variable_end") 1009 elif token.type == "block_begin": 1010 flush_data() 1011 next(self.stream) 1012 if end_tokens is not None and self.stream.current.test_any( 1013 *end_tokens 1014 ): 1015 return body 1016 rv = self.parse_statement() 1017 if isinstance(rv, list): 1018 body.extend(rv) 1019 else: 1020 body.append(rv) 1021 self.stream.expect("block_end") 1022 else: 1023 raise AssertionError("internal parsing error") 1024 1025 flush_data() 1026 finally: 1027 if end_tokens is not None: 1028 self._end_token_stack.pop() 1029 return body 1030 1031 def parse(self) -> nodes.Template: 1032 """Parse the whole template into a `Template` node.""" 1033 result = nodes.Template(self.subparse(), lineno=1) 1034 result.set_environment(self.environment) 1035 return result
This is the central parsing class Jinja uses. It's passed to extensions and can be used to parse expressions or statements.
52 def __init__( 53 self, 54 environment: "Environment", 55 source: str, 56 name: t.Optional[str] = None, 57 filename: t.Optional[str] = None, 58 state: t.Optional[str] = None, 59 ) -> None: 60 self.environment = environment 61 self.stream = environment._tokenize(source, name, filename, state) 62 self.name = name 63 self.filename = filename 64 self.closed = False 65 self.extensions: t.Dict[ 66 str, t.Callable[["Parser"], t.Union[nodes.Node, t.List[nodes.Node]]] 67 ] = {} 68 for extension in environment.iter_extensions(): 69 for tag in extension.tags: 70 self.extensions[tag] = extension.parse 71 self._last_identifier = 0 72 self._tag_stack: t.List[str] = [] 73 self._end_token_stack: t.List[t.Tuple[str, ...]] = []
75 def fail( 76 self, 77 msg: str, 78 lineno: t.Optional[int] = None, 79 exc: t.Type[TemplateSyntaxError] = TemplateSyntaxError, 80 ) -> "te.NoReturn": 81 """Convenience method that raises `exc` with the message, passed 82 line number or last line number as well as the current name and 83 filename. 84 """ 85 if lineno is None: 86 lineno = self.stream.current.lineno 87 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.
129 def fail_unknown_tag( 130 self, name: str, lineno: t.Optional[int] = None 131 ) -> "te.NoReturn": 132 """Called if the parser encounters an unknown tag. Tries to fail 133 with a human readable error message that could help to identify 134 the problem. 135 """ 136 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.
138 def fail_eof( 139 self, 140 end_tokens: t.Optional[t.Tuple[str, ...]] = None, 141 lineno: t.Optional[int] = None, 142 ) -> "te.NoReturn": 143 """Like fail_unknown_tag but for end of template situations.""" 144 stack = list(self._end_token_stack) 145 if end_tokens is not None: 146 stack.append(end_tokens) 147 self._fail_ut_eof(None, stack, lineno)
Like fail_unknown_tag but for end of template situations.
149 def is_tuple_end( 150 self, extra_end_rules: t.Optional[t.Tuple[str, ...]] = None 151 ) -> bool: 152 """Are we at the end of a tuple?""" 153 if self.stream.current.type in ("variable_end", "block_end", "rparen"): 154 return True 155 elif extra_end_rules is not None: 156 return self.stream.current.test_any(extra_end_rules) # type: ignore 157 return False
Are we at the end of a tuple?
159 def free_identifier(self, lineno: t.Optional[int] = None) -> nodes.InternalName: 160 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" 161 self._last_identifier += 1 162 rv = object.__new__(nodes.InternalName) 163 nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno) 164 return rv
Return a new free identifier as ~jinja2.nodes.InternalName.
166 def parse_statement(self) -> t.Union[nodes.Node, t.List[nodes.Node]]: 167 """Parse a single statement.""" 168 token = self.stream.current 169 if token.type != "name": 170 self.fail("tag name expected", token.lineno) 171 self._tag_stack.append(token.value) 172 pop_tag = True 173 try: 174 if token.value in _statement_keywords: 175 f = getattr(self, f"parse_{self.stream.current.value}") 176 return f() # type: ignore 177 if token.value == "call": 178 return self.parse_call_block() 179 if token.value == "filter": 180 return self.parse_filter_block() 181 ext = self.extensions.get(token.value) 182 if ext is not None: 183 return ext(self) 184 185 # did not work out, remove the token we pushed by accident 186 # from the stack so that the unknown tag fail function can 187 # produce a proper error message. 188 self._tag_stack.pop() 189 pop_tag = False 190 self.fail_unknown_tag(token.value, token.lineno) 191 finally: 192 if pop_tag: 193 self._tag_stack.pop()
Parse a single statement.
195 def parse_statements( 196 self, end_tokens: t.Tuple[str, ...], drop_needle: bool = False 197 ) -> t.List[nodes.Node]: 198 """Parse multiple statements into a list until one of the end tokens 199 is reached. This is used to parse the body of statements as it also 200 parses template data if appropriate. The parser checks first if the 201 current token is a colon and skips it if there is one. Then it checks 202 for the block end and parses until if one of the `end_tokens` is 203 reached. Per default the active token in the stream at the end of 204 the call is the matched end token. If this is not wanted `drop_needle` 205 can be set to `True` and the end token is removed. 206 """ 207 # the first token may be a colon for python compatibility 208 self.stream.skip_if("colon") 209 210 # in the future it would be possible to add whole code sections 211 # by adding some sort of end of statement token and parsing those here. 212 self.stream.expect("block_end") 213 result = self.subparse(end_tokens) 214 215 # we reached the end of the template too early, the subparser 216 # does not check for this, so we do that now 217 if self.stream.current.type == "eof": 218 self.fail_eof(end_tokens) 219 220 if drop_needle: 221 next(self.stream) 222 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.
224 def parse_set(self) -> t.Union[nodes.Assign, nodes.AssignBlock]: 225 """Parse an assign statement.""" 226 lineno = next(self.stream).lineno 227 target = self.parse_assign_target(with_namespace=True) 228 if self.stream.skip_if("assign"): 229 expr = self.parse_tuple() 230 return nodes.Assign(target, expr, lineno=lineno) 231 filter_node = self.parse_filter(None) 232 body = self.parse_statements(("name:endset",), drop_needle=True) 233 return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
Parse an assign statement.
235 def parse_for(self) -> nodes.For: 236 """Parse a for loop.""" 237 lineno = self.stream.expect("name:for").lineno 238 target = self.parse_assign_target(extra_end_rules=("name:in",)) 239 self.stream.expect("name:in") 240 iter = self.parse_tuple( 241 with_condexpr=False, extra_end_rules=("name:recursive",) 242 ) 243 test = None 244 if self.stream.skip_if("name:if"): 245 test = self.parse_expression() 246 recursive = self.stream.skip_if("name:recursive") 247 body = self.parse_statements(("name:endfor", "name:else")) 248 if next(self.stream).value == "endfor": 249 else_ = [] 250 else: 251 else_ = self.parse_statements(("name:endfor",), drop_needle=True) 252 return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno)
Parse a for loop.
254 def parse_if(self) -> nodes.If: 255 """Parse an if construct.""" 256 node = result = nodes.If(lineno=self.stream.expect("name:if").lineno) 257 while True: 258 node.test = self.parse_tuple(with_condexpr=False) 259 node.body = self.parse_statements(("name:elif", "name:else", "name:endif")) 260 node.elif_ = [] 261 node.else_ = [] 262 token = next(self.stream) 263 if token.test("name:elif"): 264 node = nodes.If(lineno=self.stream.current.lineno) 265 result.elif_.append(node) 266 continue 267 elif token.test("name:else"): 268 result.else_ = self.parse_statements(("name:endif",), drop_needle=True) 269 break 270 return result
Parse an if construct.
272 def parse_with(self) -> nodes.With: 273 node = nodes.With(lineno=next(self.stream).lineno) 274 targets: t.List[nodes.Expr] = [] 275 values: t.List[nodes.Expr] = [] 276 while self.stream.current.type != "block_end": 277 if targets: 278 self.stream.expect("comma") 279 target = self.parse_assign_target() 280 target.set_ctx("param") 281 targets.append(target) 282 self.stream.expect("assign") 283 values.append(self.parse_expression()) 284 node.targets = targets 285 node.values = values 286 node.body = self.parse_statements(("name:endwith",), drop_needle=True) 287 return node
289 def parse_autoescape(self) -> nodes.Scope: 290 node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno) 291 node.options = [nodes.Keyword("autoescape", self.parse_expression())] 292 node.body = self.parse_statements(("name:endautoescape",), drop_needle=True) 293 return nodes.Scope([node])
295 def parse_block(self) -> nodes.Block: 296 node = nodes.Block(lineno=next(self.stream).lineno) 297 node.name = self.stream.expect("name").value 298 node.scoped = self.stream.skip_if("name:scoped") 299 node.required = self.stream.skip_if("name:required") 300 301 # common problem people encounter when switching from django 302 # to jinja. we do not support hyphens in block names, so let's 303 # raise a nicer error message in that case. 304 if self.stream.current.type == "sub": 305 self.fail( 306 "Block names in Jinja have to be valid Python identifiers and may not" 307 " contain hyphens, use an underscore instead." 308 ) 309 310 node.body = self.parse_statements(("name:endblock",), drop_needle=True) 311 312 # enforce that required blocks only contain whitespace or comments 313 # by asserting that the body, if not empty, is just TemplateData nodes 314 # with whitespace data 315 if node.required: 316 for body_node in node.body: 317 if not isinstance(body_node, nodes.Output) or any( 318 not isinstance(output_node, nodes.TemplateData) 319 or not output_node.data.isspace() 320 for output_node in body_node.nodes 321 ): 322 self.fail("Required blocks can only contain comments or whitespace") 323 324 self.stream.skip_if("name:" + node.name) 325 return node
332 def parse_import_context( 333 self, node: _ImportInclude, default: bool 334 ) -> _ImportInclude: 335 if self.stream.current.test_any( 336 "name:with", "name:without" 337 ) and self.stream.look().test("name:context"): 338 node.with_context = next(self.stream).value == "with" 339 self.stream.skip() 340 else: 341 node.with_context = default 342 return node
344 def parse_include(self) -> nodes.Include: 345 node = nodes.Include(lineno=next(self.stream).lineno) 346 node.template = self.parse_expression() 347 if self.stream.current.test("name:ignore") and self.stream.look().test( 348 "name:missing" 349 ): 350 node.ignore_missing = True 351 self.stream.skip(2) 352 else: 353 node.ignore_missing = False 354 return self.parse_import_context(node, True)
363 def parse_from(self) -> nodes.FromImport: 364 node = nodes.FromImport(lineno=next(self.stream).lineno) 365 node.template = self.parse_expression() 366 self.stream.expect("name:import") 367 node.names = [] 368 369 def parse_context() -> bool: 370 if self.stream.current.value in { 371 "with", 372 "without", 373 } and self.stream.look().test("name:context"): 374 node.with_context = next(self.stream).value == "with" 375 self.stream.skip() 376 return True 377 return False 378 379 while True: 380 if node.names: 381 self.stream.expect("comma") 382 if self.stream.current.type == "name": 383 if parse_context(): 384 break 385 target = self.parse_assign_target(name_only=True) 386 if target.name.startswith("_"): 387 self.fail( 388 "names starting with an underline can not be imported", 389 target.lineno, 390 exc=TemplateAssertionError, 391 ) 392 if self.stream.skip_if("name:as"): 393 alias = self.parse_assign_target(name_only=True) 394 node.names.append((target.name, alias.name)) 395 else: 396 node.names.append(target.name) 397 if parse_context() or self.stream.current.type != "comma": 398 break 399 else: 400 self.stream.expect("name") 401 if not hasattr(node, "with_context"): 402 node.with_context = False 403 return node
405 def parse_signature(self, node: _MacroCall) -> None: 406 args = node.args = [] 407 defaults = node.defaults = [] 408 self.stream.expect("lparen") 409 while self.stream.current.type != "rparen": 410 if args: 411 self.stream.expect("comma") 412 arg = self.parse_assign_target(name_only=True) 413 arg.set_ctx("param") 414 if self.stream.skip_if("assign"): 415 defaults.append(self.parse_expression()) 416 elif defaults: 417 self.fail("non-default argument follows default argument") 418 args.append(arg) 419 self.stream.expect("rparen")
421 def parse_call_block(self) -> nodes.CallBlock: 422 node = nodes.CallBlock(lineno=next(self.stream).lineno) 423 if self.stream.current.type == "lparen": 424 self.parse_signature(node) 425 else: 426 node.args = [] 427 node.defaults = [] 428 429 call_node = self.parse_expression() 430 if not isinstance(call_node, nodes.Call): 431 self.fail("expected call", node.lineno) 432 node.call = call_node 433 node.body = self.parse_statements(("name:endcall",), drop_needle=True) 434 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(self) -> t.Tuple: 866 token = self.stream.expect("lparen") 867 args = [] 868 kwargs = [] 869 dyn_args = None 870 dyn_kwargs = None 871 require_comma = False 872 873 def ensure(expr: bool) -> None: 874 if not expr: 875 self.fail("invalid syntax for function call expression", token.lineno) 876 877 while self.stream.current.type != "rparen": 878 if require_comma: 879 self.stream.expect("comma") 880 881 # support for trailing comma 882 if self.stream.current.type == "rparen": 883 break 884 885 if self.stream.current.type == "mul": 886 ensure(dyn_args is None and dyn_kwargs is None) 887 next(self.stream) 888 dyn_args = self.parse_expression() 889 elif self.stream.current.type == "pow": 890 ensure(dyn_kwargs is None) 891 next(self.stream) 892 dyn_kwargs = self.parse_expression() 893 else: 894 if ( 895 self.stream.current.type == "name" 896 and self.stream.look().type == "assign" 897 ): 898 # Parsing a kwarg 899 ensure(dyn_kwargs is None) 900 key = self.stream.current.value 901 self.stream.skip(2) 902 value = self.parse_expression() 903 kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) 904 else: 905 # Parsing an arg 906 ensure(dyn_args is None and dyn_kwargs is None and not kwargs) 907 args.append(self.parse_expression()) 908 909 require_comma = True 910 911 self.stream.expect("rparen") 912 return args, kwargs, dyn_args, dyn_kwargs
914 def parse_call(self, node: nodes.Expr) -> nodes.Call: 915 # The lparen will be expected in parse_call_args, but the lineno 916 # needs to be recorded before the stream is advanced. 917 token = self.stream.current 918 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 919 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno)
921 def parse_filter( 922 self, node: t.Optional[nodes.Expr], start_inline: bool = False 923 ) -> t.Optional[nodes.Expr]: 924 while self.stream.current.type == "pipe" or start_inline: 925 if not start_inline: 926 next(self.stream) 927 token = self.stream.expect("name") 928 name = token.value 929 while self.stream.current.type == "dot": 930 next(self.stream) 931 name += "." + self.stream.expect("name").value 932 if self.stream.current.type == "lparen": 933 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 934 else: 935 args = [] 936 kwargs = [] 937 dyn_args = dyn_kwargs = None 938 node = nodes.Filter( 939 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 940 ) 941 start_inline = False 942 return node
944 def parse_test(self, node: nodes.Expr) -> nodes.Expr: 945 token = next(self.stream) 946 if self.stream.current.test("name:not"): 947 next(self.stream) 948 negated = True 949 else: 950 negated = False 951 name = self.stream.expect("name").value 952 while self.stream.current.type == "dot": 953 next(self.stream) 954 name += "." + self.stream.expect("name").value 955 dyn_args = dyn_kwargs = None 956 kwargs = [] 957 if self.stream.current.type == "lparen": 958 args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args() 959 elif self.stream.current.type in { 960 "name", 961 "string", 962 "integer", 963 "float", 964 "lparen", 965 "lbracket", 966 "lbrace", 967 } and not self.stream.current.test_any("name:else", "name:or", "name:and"): 968 if self.stream.current.test("name:is"): 969 self.fail("You cannot chain multiple tests with is") 970 arg_node = self.parse_primary() 971 arg_node = self.parse_postfix(arg_node) 972 args = [arg_node] 973 else: 974 args = [] 975 node = nodes.Test( 976 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno 977 ) 978 if negated: 979 node = nodes.Not(node, lineno=token.lineno) 980 return node
982 def subparse( 983 self, end_tokens: t.Optional[t.Tuple[str, ...]] = None 984 ) -> t.List[nodes.Node]: 985 body: t.List[nodes.Node] = [] 986 data_buffer: t.List[nodes.Node] = [] 987 add_data = data_buffer.append 988 989 if end_tokens is not None: 990 self._end_token_stack.append(end_tokens) 991 992 def flush_data() -> None: 993 if data_buffer: 994 lineno = data_buffer[0].lineno 995 body.append(nodes.Output(data_buffer[:], lineno=lineno)) 996 del data_buffer[:] 997 998 try: 999 while self.stream: 1000 token = self.stream.current 1001 if token.type == "data": 1002 if token.value: 1003 add_data(nodes.TemplateData(token.value, lineno=token.lineno)) 1004 next(self.stream) 1005 elif token.type == "variable_begin": 1006 next(self.stream) 1007 add_data(self.parse_tuple(with_condexpr=True)) 1008 self.stream.expect("variable_end") 1009 elif token.type == "block_begin": 1010 flush_data() 1011 next(self.stream) 1012 if end_tokens is not None and self.stream.current.test_any( 1013 *end_tokens 1014 ): 1015 return body 1016 rv = self.parse_statement() 1017 if isinstance(rv, list): 1018 body.extend(rv) 1019 else: 1020 body.append(rv) 1021 self.stream.expect("block_end") 1022 else: 1023 raise AssertionError("internal parsing error") 1024 1025 flush_data() 1026 finally: 1027 if end_tokens is not None: 1028 self._end_token_stack.pop() 1029 return body