about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/jinja2/nodes.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/jinja2/nodes.py')
-rw-r--r--.venv/lib/python3.12/site-packages/jinja2/nodes.py1206
1 files changed, 1206 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/jinja2/nodes.py b/.venv/lib/python3.12/site-packages/jinja2/nodes.py
new file mode 100644
index 00000000..2f93b90e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/jinja2/nodes.py
@@ -0,0 +1,1206 @@
+"""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: t.List[t.Tuple[str, ...]] = []
+            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