Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/_nodes/internal.py: 77%
96 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.
7from contextlib import contextmanager
8from dataclasses import dataclass, field
9from typing import Iterable, Iterator, List, Optional, Sequence, TYPE_CHECKING, Union
11from libcst._add_slots import add_slots
12from libcst._flatten_sentinel import FlattenSentinel
13from libcst._maybe_sentinel import MaybeSentinel
14from libcst._removal_sentinel import RemovalSentinel
15from libcst._types import CSTNodeT
17if TYPE_CHECKING:
18 # These are circular dependencies only used for typing purposes
19 from libcst._nodes.base import CSTNode # noqa: F401
20 from libcst._visitors import CSTVisitorT
23@add_slots
24@dataclass(frozen=False)
25class CodegenState:
26 # These are derived from a Module
27 default_indent: str
28 default_newline: str
29 provider: object = None # overridden by libcst.metadata.position_provider
31 indent_tokens: List[str] = field(default_factory=list)
32 tokens: List[str] = field(default_factory=list)
34 def increase_indent(self, value: str) -> None:
35 self.indent_tokens.append(value)
37 def decrease_indent(self) -> None:
38 self.indent_tokens.pop()
40 def add_indent_tokens(self) -> None:
41 self.tokens.extend(self.indent_tokens)
43 def add_token(self, value: str) -> None:
44 self.tokens.append(value)
46 def before_codegen(self, node: "CSTNode") -> None:
47 pass
49 def after_codegen(self, node: "CSTNode") -> None:
50 pass
52 def pop_trailing_newline(self) -> None:
53 """
54 Called by :meth:`libcst.Module._codegen_impl` at the end of the file to remove
55 the last token (a trailing newline), assuming the file isn't empty.
56 """
57 if len(self.tokens) > 0:
58 # EmptyLine and all statements generate newlines, so we can be sure that the
59 # last token (if we're not an empty file) is a newline.
60 self.tokens.pop()
62 @contextmanager
63 def record_syntactic_position(
64 self,
65 node: "CSTNode",
66 *,
67 start_node: Optional["CSTNode"] = None,
68 end_node: Optional["CSTNode"] = None,
69 ) -> Iterator[None]:
70 yield
73def visit_required(
74 parent: "CSTNode", fieldname: str, node: CSTNodeT, visitor: "CSTVisitorT"
75) -> CSTNodeT:
76 """
77 Given a node, visits the node using `visitor`. If removal is attempted by the
78 visitor, an exception is raised.
79 """
80 visitor.on_visit_attribute(parent, fieldname)
81 result = node.visit(visitor)
82 if isinstance(result, RemovalSentinel):
83 raise TypeError(
84 f"We got a RemovalSentinel while visiting a {type(node).__name__}. This "
85 + "node's parent does not allow it to be removed."
86 )
87 elif isinstance(result, FlattenSentinel):
88 raise TypeError(
89 f"We got a FlattenSentinel while visiting a {type(node).__name__}. This "
90 + "node's parent does not allow for it to be it to be replaced with a "
91 + "sequence."
92 )
94 visitor.on_leave_attribute(parent, fieldname)
95 return result
98def visit_optional(
99 parent: "CSTNode", fieldname: str, node: Optional[CSTNodeT], visitor: "CSTVisitorT"
100) -> Optional[CSTNodeT]:
101 """
102 Given an optional node, visits the node if it exists with `visitor`. If the node is
103 removed, returns None.
104 """
105 if node is None:
106 visitor.on_visit_attribute(parent, fieldname)
107 visitor.on_leave_attribute(parent, fieldname)
108 return None
109 visitor.on_visit_attribute(parent, fieldname)
110 result = node.visit(visitor)
111 if isinstance(result, FlattenSentinel):
112 raise TypeError(
113 f"We got a FlattenSentinel while visiting a {type(node).__name__}. This "
114 + "node's parent does not allow for it to be it to be replaced with a "
115 + "sequence."
116 )
117 visitor.on_leave_attribute(parent, fieldname)
118 return None if isinstance(result, RemovalSentinel) else result
121def visit_sentinel(
122 parent: "CSTNode",
123 fieldname: str,
124 node: Union[CSTNodeT, MaybeSentinel],
125 visitor: "CSTVisitorT",
126) -> Union[CSTNodeT, MaybeSentinel]:
127 """
128 Given a node that can be a real value or a sentinel value, visits the node if it
129 is real with `visitor`. If the node is removed, returns MaybeSentinel.
130 """
131 if isinstance(node, MaybeSentinel):
132 visitor.on_visit_attribute(parent, fieldname)
133 visitor.on_leave_attribute(parent, fieldname)
134 return MaybeSentinel.DEFAULT
135 visitor.on_visit_attribute(parent, fieldname)
136 result = node.visit(visitor)
137 if isinstance(result, FlattenSentinel):
138 raise TypeError(
139 f"We got a FlattenSentinel while visiting a {type(node).__name__}. This "
140 + "node's parent does not allow for it to be it to be replaced with a "
141 + "sequence."
142 )
143 visitor.on_leave_attribute(parent, fieldname)
144 return MaybeSentinel.DEFAULT if isinstance(result, RemovalSentinel) else result
147def visit_iterable(
148 parent: "CSTNode",
149 fieldname: str,
150 children: Iterable[CSTNodeT],
151 visitor: "CSTVisitorT",
152) -> Iterable[CSTNodeT]:
153 """
154 Given an iterable of children, visits each child with `visitor`, and yields the new
155 children with any `RemovalSentinel` values removed.
156 """
157 visitor.on_visit_attribute(parent, fieldname)
158 for child in children:
159 new_child = child.visit(visitor)
160 if isinstance(new_child, FlattenSentinel):
161 yield from new_child
162 elif not isinstance(new_child, RemovalSentinel):
163 yield new_child
164 visitor.on_leave_attribute(parent, fieldname)
167def visit_sequence(
168 parent: "CSTNode",
169 fieldname: str,
170 children: Sequence[CSTNodeT],
171 visitor: "CSTVisitorT",
172) -> Sequence[CSTNodeT]:
173 """
174 A convenience wrapper for `visit_iterable` that returns a sequence instead of an
175 iterable.
176 """
177 return tuple(visit_iterable(parent, fieldname, children, visitor))
180def visit_body_iterable(
181 parent: "CSTNode",
182 fieldname: str,
183 children: Sequence[CSTNodeT],
184 visitor: "CSTVisitorT",
185) -> Iterable[CSTNodeT]:
186 """
187 Similar to visit_iterable above, but capable of discarding empty SimpleStatementLine
188 nodes in order to preserve correct pass insertion behavior.
189 """
191 visitor.on_visit_attribute(parent, fieldname)
192 for child in children:
193 new_child = child.visit(visitor)
195 # Don't yield a child if we removed it.
196 if isinstance(new_child, RemovalSentinel):
197 continue
199 # Don't yield a child if the old child wasn't empty
200 # and the new child is. This means a RemovalSentinel
201 # caused a child of this node to be dropped, and it
202 # is now useless.
204 if isinstance(new_child, FlattenSentinel):
205 for child_ in new_child:
206 if (not child._is_removable()) and child_._is_removable():
207 continue
208 yield child_
209 else:
210 if (not child._is_removable()) and new_child._is_removable():
211 continue
212 # Safe to yield child in this case.
213 yield new_child
214 visitor.on_leave_attribute(parent, fieldname)
217def visit_body_sequence(
218 parent: "CSTNode",
219 fieldname: str,
220 children: Sequence[CSTNodeT],
221 visitor: "CSTVisitorT",
222) -> Sequence[CSTNodeT]:
223 """
224 A convenience wrapper for `visit_body_iterable` that returns a sequence
225 instead of an iterable.
226 """
227 return tuple(visit_body_iterable(parent, fieldname, children, visitor))