summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/jinja2/visitor.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.11/site-packages/jinja2/visitor.py')
-rw-r--r--venv/lib/python3.11/site-packages/jinja2/visitor.py92
1 files changed, 92 insertions, 0 deletions
diff --git a/venv/lib/python3.11/site-packages/jinja2/visitor.py b/venv/lib/python3.11/site-packages/jinja2/visitor.py
new file mode 100644
index 0000000..17c6aab
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/jinja2/visitor.py
@@ -0,0 +1,92 @@
+"""API for traversing the AST nodes. Implemented by the compiler and
+meta introspection.
+"""
+import typing as t
+
+from .nodes import Node
+
+if t.TYPE_CHECKING:
+ import typing_extensions as te
+
+ class VisitCallable(te.Protocol):
+ def __call__(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.Any:
+ ...
+
+
+class NodeVisitor:
+ """Walks the abstract syntax tree and call visitor functions for every
+ node found. The visitor functions may return values which will be
+ forwarded by the `visit` method.
+
+ Per default the visitor functions for the nodes are ``'visit_'`` +
+ class name of the node. So a `TryFinally` node visit function would
+ be `visit_TryFinally`. This behavior can be changed by overriding
+ the `get_visitor` function. If no visitor function exists for a node
+ (return value `None`) the `generic_visit` visitor is used instead.
+ """
+
+ def get_visitor(self, node: Node) -> "t.Optional[VisitCallable]":
+ """Return the visitor function for this node or `None` if no visitor
+ exists for this node. In that case the generic visit function is
+ used instead.
+ """
+ return getattr(self, f"visit_{type(node).__name__}", None)
+
+ def visit(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.Any:
+ """Visit a node."""
+ f = self.get_visitor(node)
+
+ if f is not None:
+ return f(node, *args, **kwargs)
+
+ return self.generic_visit(node, *args, **kwargs)
+
+ def generic_visit(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.Any:
+ """Called if no explicit visitor function exists for a node."""
+ for child_node in node.iter_child_nodes():
+ self.visit(child_node, *args, **kwargs)
+
+
+class NodeTransformer(NodeVisitor):
+ """Walks the abstract syntax tree and allows modifications of nodes.
+
+ The `NodeTransformer` will walk the AST and use the return value of the
+ visitor functions to replace or remove the old node. If the return
+ value of the visitor function is `None` the node will be removed
+ from the previous location otherwise it's replaced with the return
+ value. The return value may be the original node in which case no
+ replacement takes place.
+ """
+
+ def generic_visit(self, node: Node, *args: t.Any, **kwargs: t.Any) -> Node:
+ for field, old_value in node.iter_fields():
+ if isinstance(old_value, list):
+ new_values = []
+ for value in old_value:
+ if isinstance(value, Node):
+ value = self.visit(value, *args, **kwargs)
+ if value is None:
+ continue
+ elif not isinstance(value, Node):
+ new_values.extend(value)
+ continue
+ new_values.append(value)
+ old_value[:] = new_values
+ elif isinstance(old_value, Node):
+ new_node = self.visit(old_value, *args, **kwargs)
+ if new_node is None:
+ delattr(node, field)
+ else:
+ setattr(node, field, new_node)
+ return node
+
+ def visit_list(self, node: Node, *args: t.Any, **kwargs: t.Any) -> t.List[Node]:
+ """As transformers may return lists in some places this method
+ can be used to enforce a list as return value.
+ """
+ rv = self.visit(node, *args, **kwargs)
+
+ if not isinstance(rv, list):
+ return [rv]
+
+ return rv