1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204 |
- """AST nodes generated by the parser for the compiler. Also provides
- some node tree helper functions used by the parser and compiler in order
- to normalize nodes.
- """
- import inspect
- import operator
- import typing as t
- from collections import deque
- from markupsafe import Markup
- from .utils import _PassArg
- if t.TYPE_CHECKING:
- import typing_extensions as te
- from .environment import Environment
- _NodeBound = t.TypeVar("_NodeBound", bound="Node")
- _binop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
- "*": operator.mul,
- "/": operator.truediv,
- "//": operator.floordiv,
- "**": operator.pow,
- "%": operator.mod,
- "+": operator.add,
- "-": operator.sub,
- }
- _uaop_to_func: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
- "not": operator.not_,
- "+": operator.pos,
- "-": operator.neg,
- }
- _cmpop_to_func: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
- "eq": operator.eq,
- "ne": operator.ne,
- "gt": operator.gt,
- "gteq": operator.ge,
- "lt": operator.lt,
- "lteq": operator.le,
- "in": lambda a, b: a in b,
- "notin": lambda a, b: a not in b,
- }
- class Impossible(Exception):
- """Raised if the node could not perform a requested action."""
- class NodeType(type):
- """A metaclass for nodes that handles the field and attribute
- inheritance. fields and attributes from the parent class are
- automatically forwarded to the child."""
- def __new__(mcs, name, bases, d): # type: ignore
- for attr in "fields", "attributes":
- storage = []
- storage.extend(getattr(bases[0] if bases else object, attr, ()))
- storage.extend(d.get(attr, ()))
- assert len(bases) <= 1, "multiple inheritance not allowed"
- assert len(storage) == len(set(storage)), "layout conflict"
- d[attr] = tuple(storage)
- d.setdefault("abstract", False)
- return type.__new__(mcs, name, bases, d)
- class EvalContext:
- """Holds evaluation time information. Custom attributes can be attached
- to it in extensions.
- """
- def __init__(
- self, environment: "Environment", template_name: t.Optional[str] = None
- ) -> None:
- self.environment = environment
- if callable(environment.autoescape):
- self.autoescape = environment.autoescape(template_name)
- else:
- self.autoescape = environment.autoescape
- self.volatile = False
- def save(self) -> t.Mapping[str, t.Any]:
- return self.__dict__.copy()
- def revert(self, old: t.Mapping[str, t.Any]) -> None:
- self.__dict__.clear()
- self.__dict__.update(old)
- def get_eval_context(node: "Node", ctx: t.Optional[EvalContext]) -> EvalContext:
- if ctx is None:
- if node.environment is None:
- raise RuntimeError(
- "if no eval context is passed, the node must have an"
- " attached environment."
- )
- return EvalContext(node.environment)
- return ctx
- class Node(metaclass=NodeType):
- """Baseclass for all Jinja nodes. There are a number of nodes available
- of different types. There are four major types:
- - :class:`Stmt`: statements
- - :class:`Expr`: expressions
- - :class:`Helper`: helper nodes
- - :class:`Template`: the outermost wrapper node
- All nodes have fields and attributes. Fields may be other nodes, lists,
- or arbitrary values. Fields are passed to the constructor as regular
- positional arguments, attributes as keyword arguments. Each node has
- two attributes: `lineno` (the line number of the node) and `environment`.
- The `environment` attribute is set at the end of the parsing process for
- all nodes automatically.
- """
- fields: t.Tuple[str, ...] = ()
- attributes: t.Tuple[str, ...] = ("lineno", "environment")
- abstract = True
- lineno: int
- environment: t.Optional["Environment"]
- def __init__(self, *fields: t.Any, **attributes: t.Any) -> None:
- if self.abstract:
- raise TypeError("abstract nodes are not instantiable")
- if fields:
- if len(fields) != len(self.fields):
- if not self.fields:
- raise TypeError(f"{type(self).__name__!r} takes 0 arguments")
- raise TypeError(
- f"{type(self).__name__!r} takes 0 or {len(self.fields)}"
- f" argument{'s' if len(self.fields) != 1 else ''}"
- )
- for name, arg in zip(self.fields, fields):
- setattr(self, name, arg)
- for attr in self.attributes:
- setattr(self, attr, attributes.pop(attr, None))
- if attributes:
- raise TypeError(f"unknown attribute {next(iter(attributes))!r}")
- def iter_fields(
- self,
- exclude: t.Optional[t.Container[str]] = None,
- only: t.Optional[t.Container[str]] = None,
- ) -> t.Iterator[t.Tuple[str, t.Any]]:
- """This method iterates over all fields that are defined and yields
- ``(key, value)`` tuples. Per default all fields are returned, but
- it's possible to limit that to some fields by providing the `only`
- parameter or to exclude some using the `exclude` parameter. Both
- should be sets or tuples of field names.
- """
- for name in self.fields:
- if (
- (exclude is None and only is None)
- or (exclude is not None and name not in exclude)
- or (only is not None and name in only)
- ):
- try:
- yield name, getattr(self, name)
- except AttributeError:
- pass
- def iter_child_nodes(
- self,
- exclude: t.Optional[t.Container[str]] = None,
- only: t.Optional[t.Container[str]] = None,
- ) -> t.Iterator["Node"]:
- """Iterates over all direct child nodes of the node. This iterates
- over all fields and yields the values of they are nodes. If the value
- of a field is a list all the nodes in that list are returned.
- """
- for _, item in self.iter_fields(exclude, only):
- if isinstance(item, list):
- for n in item:
- if isinstance(n, Node):
- yield n
- elif isinstance(item, Node):
- yield item
- def find(self, node_type: t.Type[_NodeBound]) -> t.Optional[_NodeBound]:
- """Find the first node of a given type. If no such node exists the
- return value is `None`.
- """
- for result in self.find_all(node_type):
- return result
- return None
- def find_all(
- self, node_type: t.Union[t.Type[_NodeBound], t.Tuple[t.Type[_NodeBound], ...]]
- ) -> t.Iterator[_NodeBound]:
- """Find all the nodes of a given type. If the type is a tuple,
- the check is performed for any of the tuple items.
- """
- for child in self.iter_child_nodes():
- if isinstance(child, node_type):
- yield child # type: ignore
- yield from child.find_all(node_type)
- def set_ctx(self, ctx: str) -> "Node":
- """Reset the context of a node and all child nodes. Per default the
- parser will all generate nodes that have a 'load' context as it's the
- most common one. This method is used in the parser to set assignment
- targets and other nodes to a store context.
- """
- todo = deque([self])
- while todo:
- node = todo.popleft()
- if "ctx" in node.fields:
- node.ctx = ctx # type: ignore
- todo.extend(node.iter_child_nodes())
- return self
- def set_lineno(self, lineno: int, override: bool = False) -> "Node":
- """Set the line numbers of the node and children."""
- todo = deque([self])
- while todo:
- node = todo.popleft()
- if "lineno" in node.attributes:
- if node.lineno is None or override:
- node.lineno = lineno
- todo.extend(node.iter_child_nodes())
- return self
- def set_environment(self, environment: "Environment") -> "Node":
- """Set the environment for all nodes."""
- todo = deque([self])
- while todo:
- node = todo.popleft()
- node.environment = environment
- todo.extend(node.iter_child_nodes())
- return self
- def __eq__(self, other: t.Any) -> bool:
- if type(self) is not type(other):
- return NotImplemented
- return tuple(self.iter_fields()) == tuple(other.iter_fields())
- __hash__ = object.__hash__
- def __repr__(self) -> str:
- args_str = ", ".join(f"{a}={getattr(self, a, None)!r}" for a in self.fields)
- return f"{type(self).__name__}({args_str})"
- def dump(self) -> str:
- def _dump(node: t.Union[Node, t.Any]) -> None:
- if not isinstance(node, Node):
- buf.append(repr(node))
- return
- buf.append(f"nodes.{type(node).__name__}(")
- if not node.fields:
- buf.append(")")
- return
- for idx, field in enumerate(node.fields):
- if idx:
- buf.append(", ")
- value = getattr(node, field)
- if isinstance(value, list):
- buf.append("[")
- for idx, item in enumerate(value):
- if idx:
- buf.append(", ")
- _dump(item)
- buf.append("]")
- else:
- _dump(value)
- buf.append(")")
- buf: t.List[str] = []
- _dump(self)
- return "".join(buf)
- class Stmt(Node):
- """Base node for all statements."""
- abstract = True
- class Helper(Node):
- """Nodes that exist in a specific context only."""
- abstract = True
- class Template(Node):
- """Node that represents a template. This must be the outermost node that
- is passed to the compiler.
- """
- fields = ("body",)
- body: t.List[Node]
- class Output(Stmt):
- """A node that holds multiple expressions which are then printed out.
- This is used both for the `print` statement and the regular template data.
- """
- fields = ("nodes",)
- nodes: t.List["Expr"]
- class Extends(Stmt):
- """Represents an extends statement."""
- fields = ("template",)
- template: "Expr"
- class For(Stmt):
- """The for loop. `target` is the target for the iteration (usually a
- :class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list
- of nodes that are used as loop-body, and `else_` a list of nodes for the
- `else` block. If no else node exists it has to be an empty list.
- For filtered nodes an expression can be stored as `test`, otherwise `None`.
- """
- fields = ("target", "iter", "body", "else_", "test", "recursive")
- target: Node
- iter: Node
- body: t.List[Node]
- else_: t.List[Node]
- test: t.Optional[Node]
- recursive: bool
- class If(Stmt):
- """If `test` is true, `body` is rendered, else `else_`."""
- fields = ("test", "body", "elif_", "else_")
- test: Node
- body: t.List[Node]
- elif_: t.List["If"]
- else_: t.List[Node]
- class Macro(Stmt):
- """A macro definition. `name` is the name of the macro, `args` a list of
- arguments and `defaults` a list of defaults if there are any. `body` is
- a list of nodes for the macro body.
- """
- fields = ("name", "args", "defaults", "body")
- name: str
- args: t.List["Name"]
- defaults: t.List["Expr"]
- body: t.List[Node]
- class CallBlock(Stmt):
- """Like a macro without a name but a call instead. `call` is called with
- the unnamed macro as `caller` argument this node holds.
- """
- fields = ("call", "args", "defaults", "body")
- call: "Call"
- args: t.List["Name"]
- defaults: t.List["Expr"]
- body: t.List[Node]
- class FilterBlock(Stmt):
- """Node for filter sections."""
- fields = ("body", "filter")
- body: t.List[Node]
- filter: "Filter"
- class With(Stmt):
- """Specific node for with statements. In older versions of Jinja the
- with statement was implemented on the base of the `Scope` node instead.
- .. versionadded:: 2.9.3
- """
- fields = ("targets", "values", "body")
- targets: t.List["Expr"]
- values: t.List["Expr"]
- body: t.List[Node]
- class Block(Stmt):
- """A node that represents a block.
- .. versionchanged:: 3.0.0
- the `required` field was added.
- """
- fields = ("name", "body", "scoped", "required")
- name: str
- body: t.List[Node]
- scoped: bool
- required: bool
- class Include(Stmt):
- """A node that represents the include tag."""
- fields = ("template", "with_context", "ignore_missing")
- template: "Expr"
- with_context: bool
- ignore_missing: bool
- class Import(Stmt):
- """A node that represents the import tag."""
- fields = ("template", "target", "with_context")
- template: "Expr"
- target: str
- with_context: bool
- class FromImport(Stmt):
- """A node that represents the from import tag. It's important to not
- pass unsafe names to the name attribute. The compiler translates the
- attribute lookups directly into getattr calls and does *not* use the
- subscript callback of the interface. As exported variables may not
- start with double underscores (which the parser asserts) this is not a
- problem for regular Jinja code, but if this node is used in an extension
- extra care must be taken.
- The list of names may contain tuples if aliases are wanted.
- """
- fields = ("template", "names", "with_context")
- template: "Expr"
- names: t.List[t.Union[str, t.Tuple[str, str]]]
- with_context: bool
- class ExprStmt(Stmt):
- """A statement that evaluates an expression and discards the result."""
- fields = ("node",)
- node: Node
- class Assign(Stmt):
- """Assigns an expression to a target."""
- fields = ("target", "node")
- target: "Expr"
- node: Node
- class AssignBlock(Stmt):
- """Assigns a block to a target."""
- fields = ("target", "filter", "body")
- target: "Expr"
- filter: t.Optional["Filter"]
- body: t.List[Node]
- class Expr(Node):
- """Baseclass for all expressions."""
- abstract = True
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- """Return the value of the expression as constant or raise
- :exc:`Impossible` if this was not possible.
- An :class:`EvalContext` can be provided, if none is given
- a default context is created which requires the nodes to have
- an attached environment.
- .. versionchanged:: 2.4
- the `eval_ctx` parameter was added.
- """
- raise Impossible()
- def can_assign(self) -> bool:
- """Check if it's possible to assign something to this node."""
- return False
- class BinExpr(Expr):
- """Baseclass for all binary expressions."""
- fields = ("left", "right")
- left: Expr
- right: Expr
- operator: str
- abstract = True
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- eval_ctx = get_eval_context(self, eval_ctx)
- # intercepted operators cannot be folded at compile time
- if (
- eval_ctx.environment.sandboxed
- and self.operator in eval_ctx.environment.intercepted_binops # type: ignore
- ):
- raise Impossible()
- f = _binop_to_func[self.operator]
- try:
- return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx))
- except Exception as e:
- raise Impossible() from e
- class UnaryExpr(Expr):
- """Baseclass for all unary expressions."""
- fields = ("node",)
- node: Expr
- operator: str
- abstract = True
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- eval_ctx = get_eval_context(self, eval_ctx)
- # intercepted operators cannot be folded at compile time
- if (
- eval_ctx.environment.sandboxed
- and self.operator in eval_ctx.environment.intercepted_unops # type: ignore
- ):
- raise Impossible()
- f = _uaop_to_func[self.operator]
- try:
- return f(self.node.as_const(eval_ctx))
- except Exception as e:
- raise Impossible() from e
- class Name(Expr):
- """Looks up a name or stores a value in a name.
- The `ctx` of the node can be one of the following values:
- - `store`: store a value in the name
- - `load`: load that name
- - `param`: like `store` but if the name was defined as function parameter.
- """
- fields = ("name", "ctx")
- name: str
- ctx: str
- def can_assign(self) -> bool:
- return self.name not in {"true", "false", "none", "True", "False", "None"}
- class NSRef(Expr):
- """Reference to a namespace value assignment"""
- fields = ("name", "attr")
- name: str
- attr: str
- def can_assign(self) -> bool:
- # We don't need any special checks here; NSRef assignments have a
- # runtime check to ensure the target is a namespace object which will
- # have been checked already as it is created using a normal assignment
- # which goes through a `Name` node.
- return True
- class Literal(Expr):
- """Baseclass for literals."""
- abstract = True
- class Const(Literal):
- """All constant values. The parser will return this node for simple
- constants such as ``42`` or ``"foo"`` but it can be used to store more
- complex values such as lists too. Only constants with a safe
- representation (objects where ``eval(repr(x)) == x`` is true).
- """
- fields = ("value",)
- value: t.Any
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- return self.value
- @classmethod
- def from_untrusted(
- cls,
- value: t.Any,
- lineno: t.Optional[int] = None,
- environment: "t.Optional[Environment]" = None,
- ) -> "Const":
- """Return a const object if the value is representable as
- constant value in the generated code, otherwise it will raise
- an `Impossible` exception.
- """
- from .compiler import has_safe_repr
- if not has_safe_repr(value):
- raise Impossible()
- return cls(value, lineno=lineno, environment=environment)
- class TemplateData(Literal):
- """A constant template string."""
- fields = ("data",)
- data: str
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> str:
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile:
- raise Impossible()
- if eval_ctx.autoescape:
- return Markup(self.data)
- return self.data
- class Tuple(Literal):
- """For loop unpacking and some other things like multiple arguments
- for subscripts. Like for :class:`Name` `ctx` specifies if the tuple
- is used for loading the names or storing.
- """
- fields = ("items", "ctx")
- items: t.List[Expr]
- ctx: str
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Tuple[t.Any, ...]:
- eval_ctx = get_eval_context(self, eval_ctx)
- return tuple(x.as_const(eval_ctx) for x in self.items)
- def can_assign(self) -> bool:
- for item in self.items:
- if not item.can_assign():
- return False
- return True
- class List(Literal):
- """Any list literal such as ``[1, 2, 3]``"""
- fields = ("items",)
- items: t.List[Expr]
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.List[t.Any]:
- eval_ctx = get_eval_context(self, eval_ctx)
- return [x.as_const(eval_ctx) for x in self.items]
- class Dict(Literal):
- """Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of
- :class:`Pair` nodes.
- """
- fields = ("items",)
- items: t.List["Pair"]
- def as_const(
- self, eval_ctx: t.Optional[EvalContext] = None
- ) -> t.Dict[t.Any, t.Any]:
- eval_ctx = get_eval_context(self, eval_ctx)
- return dict(x.as_const(eval_ctx) for x in self.items)
- class Pair(Helper):
- """A key, value pair for dicts."""
- fields = ("key", "value")
- key: Expr
- value: Expr
- def as_const(
- self, eval_ctx: t.Optional[EvalContext] = None
- ) -> t.Tuple[t.Any, t.Any]:
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx)
- class Keyword(Helper):
- """A key, value pair for keyword arguments where key is a string."""
- fields = ("key", "value")
- key: str
- value: Expr
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Tuple[str, t.Any]:
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.key, self.value.as_const(eval_ctx)
- class CondExpr(Expr):
- """A conditional expression (inline if expression). (``{{
- foo if bar else baz }}``)
- """
- fields = ("test", "expr1", "expr2")
- test: Expr
- expr1: Expr
- expr2: t.Optional[Expr]
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- eval_ctx = get_eval_context(self, eval_ctx)
- if self.test.as_const(eval_ctx):
- return self.expr1.as_const(eval_ctx)
- # if we evaluate to an undefined object, we better do that at runtime
- if self.expr2 is None:
- raise Impossible()
- return self.expr2.as_const(eval_ctx)
- def args_as_const(
- node: t.Union["_FilterTestCommon", "Call"], eval_ctx: t.Optional[EvalContext]
- ) -> t.Tuple[t.List[t.Any], t.Dict[t.Any, t.Any]]:
- args = [x.as_const(eval_ctx) for x in node.args]
- kwargs = dict(x.as_const(eval_ctx) for x in node.kwargs)
- if node.dyn_args is not None:
- try:
- args.extend(node.dyn_args.as_const(eval_ctx))
- except Exception as e:
- raise Impossible() from e
- if node.dyn_kwargs is not None:
- try:
- kwargs.update(node.dyn_kwargs.as_const(eval_ctx))
- except Exception as e:
- raise Impossible() from e
- return args, kwargs
- class _FilterTestCommon(Expr):
- fields = ("node", "name", "args", "kwargs", "dyn_args", "dyn_kwargs")
- node: Expr
- name: str
- args: t.List[Expr]
- kwargs: t.List[Pair]
- dyn_args: t.Optional[Expr]
- dyn_kwargs: t.Optional[Expr]
- abstract = True
- _is_filter = True
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile:
- raise Impossible()
- if self._is_filter:
- env_map = eval_ctx.environment.filters
- else:
- env_map = eval_ctx.environment.tests
- func = env_map.get(self.name)
- pass_arg = _PassArg.from_obj(func) # type: ignore
- if func is None or pass_arg is _PassArg.context:
- raise Impossible()
- if eval_ctx.environment.is_async and (
- getattr(func, "jinja_async_variant", False) is True
- or inspect.iscoroutinefunction(func)
- ):
- raise Impossible()
- args, kwargs = args_as_const(self, eval_ctx)
- args.insert(0, self.node.as_const(eval_ctx))
- if pass_arg is _PassArg.eval_context:
- args.insert(0, eval_ctx)
- elif pass_arg is _PassArg.environment:
- args.insert(0, eval_ctx.environment)
- try:
- return func(*args, **kwargs)
- except Exception as e:
- raise Impossible() from e
- class Filter(_FilterTestCommon):
- """Apply a filter to an expression. ``name`` is the name of the
- filter, the other fields are the same as :class:`Call`.
- If ``node`` is ``None``, the filter is being used in a filter block
- and is applied to the content of the block.
- """
- node: t.Optional[Expr] # type: ignore
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- if self.node is None:
- raise Impossible()
- return super().as_const(eval_ctx=eval_ctx)
- class Test(_FilterTestCommon):
- """Apply a test to an expression. ``name`` is the name of the test,
- the other field are the same as :class:`Call`.
- .. versionchanged:: 3.0
- ``as_const`` shares the same logic for filters and tests. Tests
- check for volatile, async, and ``@pass_context`` etc.
- decorators.
- """
- _is_filter = False
- class Call(Expr):
- """Calls an expression. `args` is a list of arguments, `kwargs` a list
- of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args`
- and `dyn_kwargs` has to be either `None` or a node that is used as
- node for dynamic positional (``*args``) or keyword (``**kwargs``)
- arguments.
- """
- fields = ("node", "args", "kwargs", "dyn_args", "dyn_kwargs")
- node: Expr
- args: t.List[Expr]
- kwargs: t.List[Keyword]
- dyn_args: t.Optional[Expr]
- dyn_kwargs: t.Optional[Expr]
- class Getitem(Expr):
- """Get an attribute or item from an expression and prefer the item."""
- fields = ("node", "arg", "ctx")
- node: Expr
- arg: Expr
- ctx: str
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- if self.ctx != "load":
- raise Impossible()
- eval_ctx = get_eval_context(self, eval_ctx)
- try:
- return eval_ctx.environment.getitem(
- self.node.as_const(eval_ctx), self.arg.as_const(eval_ctx)
- )
- except Exception as e:
- raise Impossible() from e
- class Getattr(Expr):
- """Get an attribute or item from an expression that is a ascii-only
- bytestring and prefer the attribute.
- """
- fields = ("node", "attr", "ctx")
- node: Expr
- attr: str
- ctx: str
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- if self.ctx != "load":
- raise Impossible()
- eval_ctx = get_eval_context(self, eval_ctx)
- try:
- return eval_ctx.environment.getattr(self.node.as_const(eval_ctx), self.attr)
- except Exception as e:
- raise Impossible() from e
- class Slice(Expr):
- """Represents a slice object. This must only be used as argument for
- :class:`Subscript`.
- """
- fields = ("start", "stop", "step")
- start: t.Optional[Expr]
- stop: t.Optional[Expr]
- step: t.Optional[Expr]
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> slice:
- eval_ctx = get_eval_context(self, eval_ctx)
- def const(obj: t.Optional[Expr]) -> t.Optional[t.Any]:
- if obj is None:
- return None
- return obj.as_const(eval_ctx)
- return slice(const(self.start), const(self.stop), const(self.step))
- class Concat(Expr):
- """Concatenates the list of expressions provided after converting
- them to strings.
- """
- fields = ("nodes",)
- nodes: t.List[Expr]
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> str:
- eval_ctx = get_eval_context(self, eval_ctx)
- return "".join(str(x.as_const(eval_ctx)) for x in self.nodes)
- class Compare(Expr):
- """Compares an expression with some other expressions. `ops` must be a
- list of :class:`Operand`\\s.
- """
- fields = ("expr", "ops")
- expr: Expr
- ops: t.List["Operand"]
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- eval_ctx = get_eval_context(self, eval_ctx)
- result = value = self.expr.as_const(eval_ctx)
- try:
- for op in self.ops:
- new_value = op.expr.as_const(eval_ctx)
- result = _cmpop_to_func[op.op](value, new_value)
- if not result:
- return False
- value = new_value
- except Exception as e:
- raise Impossible() from e
- return result
- class Operand(Helper):
- """Holds an operator and an expression."""
- fields = ("op", "expr")
- op: str
- expr: Expr
- class Mul(BinExpr):
- """Multiplies the left with the right node."""
- operator = "*"
- class Div(BinExpr):
- """Divides the left by the right node."""
- operator = "/"
- class FloorDiv(BinExpr):
- """Divides the left by the right node and converts the
- result into an integer by truncating.
- """
- operator = "//"
- class Add(BinExpr):
- """Add the left to the right node."""
- operator = "+"
- class Sub(BinExpr):
- """Subtract the right from the left node."""
- operator = "-"
- class Mod(BinExpr):
- """Left modulo right."""
- operator = "%"
- class Pow(BinExpr):
- """Left to the power of right."""
- operator = "**"
- class And(BinExpr):
- """Short circuited AND."""
- operator = "and"
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx)
- class Or(BinExpr):
- """Short circuited OR."""
- operator = "or"
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> t.Any:
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx)
- class Not(UnaryExpr):
- """Negate the expression."""
- operator = "not"
- class Neg(UnaryExpr):
- """Make the expression negative."""
- operator = "-"
- class Pos(UnaryExpr):
- """Make the expression positive (noop for most expressions)"""
- operator = "+"
- # Helpers for extensions
- class EnvironmentAttribute(Expr):
- """Loads an attribute from the environment object. This is useful for
- extensions that want to call a callback stored on the environment.
- """
- fields = ("name",)
- name: str
- class ExtensionAttribute(Expr):
- """Returns the attribute of an extension bound to the environment.
- The identifier is the identifier of the :class:`Extension`.
- This node is usually constructed by calling the
- :meth:`~jinja2.ext.Extension.attr` method on an extension.
- """
- fields = ("identifier", "name")
- identifier: str
- name: str
- class ImportedName(Expr):
- """If created with an import name the import name is returned on node
- access. For example ``ImportedName('cgi.escape')`` returns the `escape`
- function from the cgi module on evaluation. Imports are optimized by the
- compiler so there is no need to assign them to local variables.
- """
- fields = ("importname",)
- importname: str
- class InternalName(Expr):
- """An internal name in the compiler. You cannot create these nodes
- yourself but the parser provides a
- :meth:`~jinja2.parser.Parser.free_identifier` method that creates
- a new identifier for you. This identifier is not available from the
- template and is not treated specially by the compiler.
- """
- fields = ("name",)
- name: str
- def __init__(self) -> None:
- raise TypeError(
- "Can't create internal names. Use the "
- "`free_identifier` method on a parser."
- )
- class MarkSafe(Expr):
- """Mark the wrapped expression as safe (wrap it as `Markup`)."""
- fields = ("expr",)
- expr: Expr
- def as_const(self, eval_ctx: t.Optional[EvalContext] = None) -> Markup:
- eval_ctx = get_eval_context(self, eval_ctx)
- return Markup(self.expr.as_const(eval_ctx))
- class MarkSafeIfAutoescape(Expr):
- """Mark the wrapped expression as safe (wrap it as `Markup`) but
- only if autoescaping is active.
- .. versionadded:: 2.5
- """
- fields = ("expr",)
- expr: Expr
- def as_const(
- self, eval_ctx: t.Optional[EvalContext] = None
- ) -> t.Union[Markup, t.Any]:
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile:
- raise Impossible()
- expr = self.expr.as_const(eval_ctx)
- if eval_ctx.autoescape:
- return Markup(expr)
- return expr
- class ContextReference(Expr):
- """Returns the current template context. It can be used like a
- :class:`Name` node, with a ``'load'`` ctx and will return the
- current :class:`~jinja2.runtime.Context` object.
- Here an example that assigns the current template name to a
- variable named `foo`::
- Assign(Name('foo', ctx='store'),
- Getattr(ContextReference(), 'name'))
- This is basically equivalent to using the
- :func:`~jinja2.pass_context` decorator when using the high-level
- API, which causes a reference to the context to be passed as the
- first argument to a function.
- """
- class DerivedContextReference(Expr):
- """Return the current template context including locals. Behaves
- exactly like :class:`ContextReference`, but includes local
- variables, such as from a ``for`` loop.
- .. versionadded:: 2.11
- """
- class Continue(Stmt):
- """Continue a loop."""
- class Break(Stmt):
- """Break a loop."""
- class Scope(Stmt):
- """An artificial scope."""
- fields = ("body",)
- body: t.List[Node]
- class OverlayScope(Stmt):
- """An overlay scope for extensions. This is a largely unoptimized scope
- that however can be used to introduce completely arbitrary variables into
- a sub scope from a dictionary or dictionary like object. The `context`
- field has to evaluate to a dictionary object.
- Example usage::
- OverlayScope(context=self.call_method('get_context'),
- body=[...])
- .. versionadded:: 2.10
- """
- fields = ("context", "body")
- context: Expr
- body: t.List[Node]
- class EvalContextModifier(Stmt):
- """Modifies the eval context. For each option that should be modified,
- a :class:`Keyword` has to be added to the :attr:`options` list.
- Example to change the `autoescape` setting::
- EvalContextModifier(options=[Keyword('autoescape', Const(True))])
- """
- fields = ("options",)
- options: t.List[Keyword]
- class ScopedEvalContextModifier(EvalContextModifier):
- """Modifies the eval context and reverts it later. Works exactly like
- :class:`EvalContextModifier` but will only modify the
- :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`.
- """
- fields = ("body",)
- body: t.List[Node]
- # make sure nobody creates custom nodes
- def _failing_new(*args: t.Any, **kwargs: t.Any) -> "te.NoReturn":
- raise TypeError("can't create custom node types")
- NodeType.__new__ = staticmethod(_failing_new) # type: ignore
- del _failing_new
|