Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/_batched_visitor.py: 38%
55 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:43 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:43 +0000
1# Copyright (c) Meta Platforms, Inc. and affiliates.
2#
3# This source code is licensed under the MIT license found in the
4# LICENSE file in the root directory of this source tree.
6import inspect
7from typing import (
8 Callable,
9 cast,
10 Iterable,
11 List,
12 Mapping,
13 MutableMapping,
14 Optional,
15 TYPE_CHECKING,
16)
18from libcst._metadata_dependent import MetadataDependent
19from libcst._typed_visitor import CSTTypedVisitorFunctions
20from libcst._visitors import CSTNodeT, CSTVisitor
22if TYPE_CHECKING:
23 from libcst._nodes.base import CSTNode # noqa: F401
25VisitorMethod = Callable[["CSTNode"], None]
26_VisitorMethodCollection = Mapping[str, List[VisitorMethod]]
29class BatchableCSTVisitor(CSTTypedVisitorFunctions, MetadataDependent):
30 """
31 The low-level base visitor class for traversing a CST as part of a batched
32 set of traversals. This should be used in conjunction with the
33 :func:`~libcst.visit_batched` function or the
34 :func:`~libcst.MetadataWrapper.visit_batched` method from
35 :class:`~libcst.MetadataWrapper` to visit a tree.
36 Instances of this class cannot modify the tree.
37 """
39 def get_visitors(self) -> Mapping[str, VisitorMethod]:
40 """
41 Returns a mapping of all the ``visit_<Type[CSTNode]>``,
42 ``visit_<Type[CSTNode]>_<attribute>``, ``leave_<Type[CSTNode]>`` and
43 `leave_<Type[CSTNode]>_<attribute>`` methods defined by this visitor,
44 excluding all empty stubs.
45 """
47 methods = inspect.getmembers(
48 self,
49 lambda m: (
50 inspect.ismethod(m)
51 and (m.__name__.startswith("visit_") or m.__name__.startswith("leave_"))
52 and not getattr(m, "_is_no_op", False)
53 ),
54 )
56 # TODO: verify all visitor methods reference valid node classes.
57 # for name, __ in methods:
58 # ...
60 return dict(methods)
63def visit_batched(
64 node: CSTNodeT,
65 batchable_visitors: Iterable[BatchableCSTVisitor],
66 before_visit: Optional[VisitorMethod] = None,
67 after_leave: Optional[VisitorMethod] = None,
68) -> CSTNodeT:
69 """
70 Do a batched traversal over ``node`` with all ``visitors``.
72 ``before_visit`` and ``after_leave`` are provided as optional hooks to
73 execute before the ``visit_<Type[CSTNode]>`` and after the
74 ``leave_<Type[CSTNode]>`` methods from each visitor in ``visitor`` are
75 executed by the batched visitor.
77 This function does not handle metadata dependency resolution for ``visitors``.
78 See :func:`~libcst.MetadataWrapper.visit_batched` from
79 :class:`~libcst.MetadataWrapper` for batched traversal with metadata dependency
80 resolution.
81 """
82 visitor_methods = _get_visitor_methods(batchable_visitors)
83 batched_visitor = _BatchedCSTVisitor(
84 visitor_methods, before_visit=before_visit, after_leave=after_leave
85 )
86 return cast(CSTNodeT, node.visit(batched_visitor))
89def _get_visitor_methods(
90 batchable_visitors: Iterable[BatchableCSTVisitor],
91) -> _VisitorMethodCollection:
92 """
93 Gather all ``visit_<Type[CSTNode]>``, ``visit_<Type[CSTNode]>_<attribute>``,
94 ``leave_<Type[CSTNode]>`` amd `leave_<Type[CSTNode]>_<attribute>`` methods
95 from ``batchabled_visitors``.
96 """
97 visitor_methods: MutableMapping[str, List[VisitorMethod]] = {}
98 for bv in batchable_visitors:
99 for name, fn in bv.get_visitors().items():
100 visitor_methods.setdefault(name, []).append(fn)
101 return visitor_methods
104class _BatchedCSTVisitor(CSTVisitor):
105 """
106 Internal visitor class to perform batched traversal over a tree.
107 """
109 visitor_methods: _VisitorMethodCollection
110 before_visit: Optional[VisitorMethod]
111 after_leave: Optional[VisitorMethod]
113 def __init__(
114 self,
115 visitor_methods: _VisitorMethodCollection,
116 *,
117 before_visit: Optional[VisitorMethod] = None,
118 after_leave: Optional[VisitorMethod] = None,
119 ) -> None:
120 super().__init__()
121 self.visitor_methods = visitor_methods
122 self.before_visit = before_visit
123 self.after_leave = after_leave
125 def on_visit(self, node: "CSTNode") -> bool:
126 """
127 Call appropriate visit methods on node before visiting children.
128 """
129 before_visit = self.before_visit
130 if before_visit is not None:
131 before_visit(node)
132 type_name = type(node).__name__
133 for v in self.visitor_methods.get(f"visit_{type_name}", []):
134 v(node)
135 return True
137 def on_leave(self, original_node: "CSTNode") -> None:
138 """
139 Call appropriate leave methods on node after visiting children.
140 """
141 type_name = type(original_node).__name__
142 for v in self.visitor_methods.get(f"leave_{type_name}", []):
143 v(original_node)
144 after_leave = self.after_leave
145 if after_leave is not None:
146 after_leave(original_node)
148 def on_visit_attribute(self, node: "CSTNode", attribute: str) -> None:
149 """
150 Call appropriate visit attribute methods on node before visiting
151 attribute's children.
152 """
153 type_name = type(node).__name__
154 for v in self.visitor_methods.get(f"visit_{type_name}_{attribute}", []):
155 v(node)
157 def on_leave_attribute(self, original_node: "CSTNode", attribute: str) -> None:
158 """
159 Call appropriate leave attribute methods on node after visiting
160 attribute's children.
161 """
162 type_name = type(original_node).__name__
163 for v in self.visitor_methods.get(f"leave_{type_name}_{attribute}", []):
164 v(original_node)