Source code for libcst._visitors
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from typing import TYPE_CHECKING, Union
from libcst._flatten_sentinel import FlattenSentinel
from libcst._metadata_dependent import MetadataDependent
from libcst._removal_sentinel import RemovalSentinel
from libcst._typed_visitor import CSTTypedTransformerFunctions, CSTTypedVisitorFunctions
from libcst._types import CSTNodeT
if TYPE_CHECKING:
# Circular dependency for typing reasons only
from libcst._nodes.base import CSTNode # noqa: F401
CSTVisitorT = Union["CSTTransformer", "CSTVisitor"]
[docs]class CSTTransformer(CSTTypedTransformerFunctions, MetadataDependent):
"""
The low-level base visitor class for traversing a CST and creating an
updated copy of the original CST. This should be used in conjunction with
the :func:`~libcst.CSTNode.visit` method on a :class:`~libcst.CSTNode` to
visit each element in a tree starting with that node, and possibly returning
a new node in its place.
When visiting nodes using a :class:`CSTTransformer`, the return value of
:func:`~libcst.CSTNode.visit` will be a new tree with any changes made in
:func:`~libcst.CSTTransformer.on_leave` calls reflected in its children.
"""
[docs] def on_visit(self, node: "CSTNode") -> bool:
"""
Called every time a node is visited, before we've visited its children.
Returns ``True`` if children should be visited, and returns ``False``
otherwise.
"""
visit_func = getattr(self, f"visit_{type(node).__name__}", None)
if visit_func is not None:
retval = visit_func(node)
else:
retval = True
# Don't visit children IFF the visit function returned False.
return False if retval is False else True
[docs] def on_leave(
self, original_node: CSTNodeT, updated_node: CSTNodeT
) -> Union[CSTNodeT, RemovalSentinel, FlattenSentinel[CSTNodeT]]:
"""
Called every time we leave a node, after we've visited its children. If
the :func:`~libcst.CSTTransformer.on_visit` function for this node returns
``False``, this function will still be called on that node.
``original_node`` is guaranteed to be the same node as is passed to
:func:`~libcst.CSTTransformer.on_visit`, so it is safe to do state-based
checks using the ``is`` operator. Modifications should always be performed
on the ``updated_node`` so as to not overwrite changes made by child
visits.
Returning :attr:`RemovalSentinel.REMOVE` indicates that the node should be
removed from its parent. This is not always possible, and may raise an
exception if this node is required. As a convenience, you can use
:func:`RemoveFromParent` as an alias to :attr:`RemovalSentinel.REMOVE`.
"""
leave_func = getattr(self, f"leave_{type(original_node).__name__}", None)
if leave_func is not None:
updated_node = leave_func(original_node, updated_node)
return updated_node
[docs] def on_visit_attribute(self, node: "CSTNode", attribute: str) -> None:
"""
Called before a node's child attribute is visited and after we have called
:func:`~libcst.CSTTransformer.on_visit` on the node. A node's child
attributes are visited in the order that they appear in source that this
node originates from.
"""
visit_func = getattr(self, f"visit_{type(node).__name__}_{attribute}", None)
if visit_func is not None:
visit_func(node)
[docs] def on_leave_attribute(self, original_node: "CSTNode", attribute: str) -> None:
"""
Called after a node's child attribute is visited and before we have called
:func:`~libcst.CSTTransformer.on_leave` on the node.
Unlike :func:`~libcst.CSTTransformer.on_leave`, this function does
not allow modifications to the tree and is provided solely for state
management.
"""
leave_func = getattr(
self, f"leave_{type(original_node).__name__}_{attribute}", None
)
if leave_func is not None:
leave_func(original_node)
[docs]class CSTVisitor(CSTTypedVisitorFunctions, MetadataDependent):
"""
The low-level base visitor class for traversing a CST. This should be used in
conjunction with the :func:`~libcst.CSTNode.visit` method on a
:class:`~libcst.CSTNode` to visit each element in a tree starting with that
node. Unlike :class:`CSTTransformer`, instances of this class cannot modify
the tree.
When visiting nodes using a :class:`CSTVisitor`, the return value of
:func:`~libcst.CSTNode.visit` will equal the passed in tree.
"""
[docs] def on_visit(self, node: "CSTNode") -> bool:
"""
Called every time a node is visited, before we've visited its children.
Returns ``True`` if children should be visited, and returns ``False``
otherwise.
"""
visit_func = getattr(self, f"visit_{type(node).__name__}", None)
if visit_func is not None:
retval = visit_func(node)
else:
retval = True
# Don't visit children IFF the visit function returned False.
return False if retval is False else True
[docs] def on_leave(self, original_node: "CSTNode") -> None:
"""
Called every time we leave a node, after we've visited its children. If
the :func:`~libcst.CSTVisitor.on_visit` function for this node returns
``False``, this function will still be called on that node.
"""
leave_func = getattr(self, f"leave_{type(original_node).__name__}", None)
if leave_func is not None:
leave_func(original_node)
[docs] def on_visit_attribute(self, node: "CSTNode", attribute: str) -> None:
"""
Called before a node's child attribute is visited and after we have called
:func:`~libcst.CSTTransformer.on_visit` on the node. A node's child
attributes are visited in the order that they appear in source that this
node originates from.
"""
visit_func = getattr(self, f"visit_{type(node).__name__}_{attribute}", None)
if visit_func is not None:
visit_func(node)
[docs] def on_leave_attribute(self, original_node: "CSTNode", attribute: str) -> None:
"""
Called after a node's child attribute is visited and before we have called
:func:`~libcst.CSTVisitor.on_leave` on the node.
"""
leave_func = getattr(
self, f"leave_{type(original_node).__name__}_{attribute}", None
)
if leave_func is not None:
leave_func(original_node)