]>
crepu.dev Git - config.git/blob - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/yapf/pytree/pytree_unwrapper.py
1 # Copyright 2015 Google Inc. All Rights Reserved.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """PyTreeUnwrapper - produces a list of logical lines from a pytree.
16 [for a description of what a logical line is, see logical_line.py]
18 This is a pytree visitor that goes over a parse tree and produces a list of
19 LogicalLine containers from it, each with its own depth and containing all the
20 tokens that could fit on the line if there were no maximal line-length
23 Note: a precondition to running this visitor and obtaining correct results is
24 for the tree to have its comments spliced in as nodes. Prefixes are ignored.
26 For most uses, the convenience function UnwrapPyTree should be sufficient.
29 # The word "token" is overloaded within this module, so for clarity rename
30 # the imported pgen2.token module.
31 from yapf_third_party
._ylib
2to
3 import pytree
32 from yapf_third_party
._ylib
2to
3.pgen2
import token
as grammar_token
34 from yapf
.pytree
import pytree_utils
35 from yapf
.pytree
import pytree_visitor
36 from yapf
.pytree
import split_penalty
37 from yapf
.yapflib
import format_token
38 from yapf
.yapflib
import logical_line
39 from yapf
.yapflib
import object_state
40 from yapf
.yapflib
import style
41 from yapf
.yapflib
import subtypes
43 _OPENING_BRACKETS
= frozenset({'(', '[', '{'})
44 _CLOSING_BRACKETS
= frozenset({')', ']', '}'})
47 def UnwrapPyTree(tree
):
48 """Create and return a list of logical lines from the given pytree.
51 tree: the top-level pytree node to unwrap..
54 A list of LogicalLine objects.
56 unwrapper
= PyTreeUnwrapper()
58 llines
= unwrapper
.GetLogicalLines()
59 llines
.sort(key
=lambda x
: x
.lineno
)
63 # Grammar tokens considered as whitespace for the purpose of unwrapping.
64 _WHITESPACE_TOKENS
= frozenset([
65 grammar_token
.NEWLINE
, grammar_token
.DEDENT
, grammar_token
.INDENT
,
66 grammar_token
.ENDMARKER
70 class PyTreeUnwrapper(pytree_visitor
.PyTreeVisitor
):
71 """PyTreeUnwrapper - see file-level docstring for detailed description.
73 Note: since this implements PyTreeVisitor and node names in lib2to3 are
74 underscore_separated, the visiting methods of this class are named as
75 Visit_node_name. invalid-name pragmas are added to each such method to silence
76 a style warning. This is forced on us by the usage of lib2to3, and re-munging
77 method names to make them different from actual node names sounded like a
78 confusing and brittle affair that wasn't worth it for this small & controlled
79 deviation from the style guide.
81 To understand the connection between visitor methods in this class, some
82 familiarity with the Python grammar is required.
86 # A list of all logical lines finished visiting so far.
87 self
._logical
_lines
= []
89 # Builds up a "current" logical line while visiting pytree nodes. Some nodes
90 # will finish a line and start a new one.
91 self
._cur
_logical
_line
= logical_line
.LogicalLine(0)
93 # Current indentation depth.
96 def GetLogicalLines(self
):
97 """Fetch the result of the tree walk.
99 Note: only call this after visiting the whole tree.
102 A list of LogicalLine objects.
104 # Make sure the last line that was being populated is flushed.
106 return self
._logical
_lines
108 def _StartNewLine(self
):
109 """Finish current line and start a new one.
111 Place the currently accumulated line into the _logical_lines list and
114 if self
._cur
_logical
_line
.tokens
:
115 self
._logical
_lines
.append(self
._cur
_logical
_line
)
116 _MatchBrackets(self
._cur
_logical
_line
)
117 _IdentifyParameterLists(self
._cur
_logical
_line
)
118 _AdjustSplitPenalty(self
._cur
_logical
_line
)
119 self
._cur
_logical
_line
= logical_line
.LogicalLine(self
._cur
_depth
)
121 _STMT_TYPES
= frozenset({
134 # pylint: disable=invalid-name,missing-docstring
135 def Visit_simple_stmt(self
, node
):
136 # A 'simple_stmt' conveniently represents a non-compound Python statement,
137 # i.e. a statement that does not contain other statements.
139 # When compound nodes have a single statement as their suite, the parser
140 # can leave it in the tree directly without creating a suite. But we have
141 # to increase depth in these cases as well. However, don't increase the
142 # depth of we have a simple_stmt that's a comment node. This represents a
143 # standalone comment and in the case of it coming directly after the
144 # funcdef, it is a "top" comment for the whole function.
145 # TODO(eliben): add more relevant compound statements here.
146 single_stmt_suite
= (
147 node
.parent
and pytree_utils
.NodeName(node
.parent
) in self
._STMT
_TYPES
)
148 is_comment_stmt
= pytree_utils
.IsCommentStatement(node
)
149 is_inside_match
= node
.parent
and pytree_utils
.NodeName(
150 node
.parent
) == 'match_stmt'
151 if (single_stmt_suite
and not is_comment_stmt
) or is_inside_match
:
154 self
.DefaultNodeVisit(node
)
155 if (single_stmt_suite
and not is_comment_stmt
) or is_inside_match
:
158 def _VisitCompoundStatement(self
, node
, substatement_names
):
159 """Helper for visiting compound statements.
161 Python compound statements serve as containers for other statements. Thus,
162 when we encounter a new compound statement, we start a new logical line.
165 node: the node to visit.
166 substatement_names: set of node names. A compound statement will be
167 recognized as a NAME node with a name in this set.
169 for child
in node
.children
:
170 # A pytree is structured in such a way that a single 'if_stmt' node will
171 # contain all the 'if', 'elif' and 'else' nodes as children (similar
172 # structure applies to 'while' statements, 'try' blocks, etc). Therefore,
173 # we visit all children here and create a new line before the requested
175 if (child
.type == grammar_token
.NAME
and
176 child
.value
in substatement_names
):
180 _IF_STMT_ELEMS
= frozenset({'if', 'else', 'elif'})
182 def Visit_if_stmt(self
, node
): # pylint: disable=invalid-name
183 self
._VisitCompoundStatement
(node
, self
._IF
_STMT
_ELEMS
)
185 _WHILE_STMT_ELEMS
= frozenset({'while', 'else'})
187 def Visit_while_stmt(self
, node
): # pylint: disable=invalid-name
188 self
._VisitCompoundStatement
(node
, self
._WHILE
_STMT
_ELEMS
)
190 _FOR_STMT_ELEMS
= frozenset({'for', 'else'})
192 def Visit_for_stmt(self
, node
): # pylint: disable=invalid-name
193 self
._VisitCompoundStatement
(node
, self
._FOR
_STMT
_ELEMS
)
195 _TRY_STMT_ELEMS
= frozenset({'try', 'except', 'else', 'finally'})
197 def Visit_try_stmt(self
, node
): # pylint: disable=invalid-name
198 self
._VisitCompoundStatement
(node
, self
._TRY
_STMT
_ELEMS
)
200 _EXCEPT_STMT_ELEMS
= frozenset({'except'})
202 def Visit_except_clause(self
, node
): # pylint: disable=invalid-name
203 self
._VisitCompoundStatement
(node
, self
._EXCEPT
_STMT
_ELEMS
)
205 _FUNC_DEF_ELEMS
= frozenset({'def'})
207 def Visit_funcdef(self
, node
): # pylint: disable=invalid-name
208 self
._VisitCompoundStatement
(node
, self
._FUNC
_DEF
_ELEMS
)
210 def Visit_async_funcdef(self
, node
): # pylint: disable=invalid-name
213 for child
in node
.children
:
216 if child
.type == grammar_token
.ASYNC
:
218 for child
in node
.children
[index
].children
:
221 _CLASS_DEF_ELEMS
= frozenset({'class'})
223 def Visit_classdef(self
, node
): # pylint: disable=invalid-name
224 self
._VisitCompoundStatement
(node
, self
._CLASS
_DEF
_ELEMS
)
226 def Visit_async_stmt(self
, node
): # pylint: disable=invalid-name
229 for child
in node
.children
:
232 if child
.type == grammar_token
.ASYNC
:
234 for child
in node
.children
[index
].children
:
235 if child
.type == grammar_token
.NAME
and child
.value
== 'else':
239 def Visit_decorator(self
, node
): # pylint: disable=invalid-name
240 for child
in node
.children
:
242 if child
.type == grammar_token
.COMMENT
and child
== node
.children
[0]:
245 def Visit_decorators(self
, node
): # pylint: disable=invalid-name
246 for child
in node
.children
:
250 def Visit_decorated(self
, node
): # pylint: disable=invalid-name
251 for child
in node
.children
:
255 _WITH_STMT_ELEMS
= frozenset({'with'})
257 def Visit_with_stmt(self
, node
): # pylint: disable=invalid-name
258 self
._VisitCompoundStatement
(node
, self
._WITH
_STMT
_ELEMS
)
260 _MATCH_STMT_ELEMS
= frozenset({'match', 'case'})
262 def Visit_match_stmt(self
, node
): # pylint: disable=invalid-name
263 self
._VisitCompoundStatement
(node
, self
._MATCH
_STMT
_ELEMS
)
265 # case_block refers to the grammar element name in Grammar.txt
266 _CASE_BLOCK_ELEMS
= frozenset({'case'})
268 def Visit_case_block(self
, node
):
271 self
._VisitCompoundStatement
(node
, self
._CASE
_BLOCK
_ELEMS
)
274 def Visit_suite(self
, node
): # pylint: disable=invalid-name
275 # A 'suite' starts a new indentation level in Python.
278 self
.DefaultNodeVisit(node
)
281 def Visit_listmaker(self
, node
): # pylint: disable=invalid-name
282 _DetermineMustSplitAnnotation(node
)
283 self
.DefaultNodeVisit(node
)
285 def Visit_dictsetmaker(self
, node
): # pylint: disable=invalid-name
286 _DetermineMustSplitAnnotation(node
)
287 self
.DefaultNodeVisit(node
)
289 def Visit_import_as_names(self
, node
): # pylint: disable=invalid-name
290 if node
.prev_sibling
.value
== '(':
291 _DetermineMustSplitAnnotation(node
)
292 self
.DefaultNodeVisit(node
)
294 def Visit_testlist_gexp(self
, node
): # pylint: disable=invalid-name
295 _DetermineMustSplitAnnotation(node
)
296 self
.DefaultNodeVisit(node
)
298 def Visit_arglist(self
, node
): # pylint: disable=invalid-name
299 _DetermineMustSplitAnnotation(node
)
300 self
.DefaultNodeVisit(node
)
302 def Visit_typedargslist(self
, node
): # pylint: disable=invalid-name
303 _DetermineMustSplitAnnotation(node
)
304 self
.DefaultNodeVisit(node
)
306 def Visit_subscriptlist(self
, node
): # pylint: disable=invalid-name
307 _DetermineMustSplitAnnotation(node
)
308 self
.DefaultNodeVisit(node
)
310 def DefaultLeafVisit(self
, leaf
):
311 """Default visitor for tree leaves.
313 A tree leaf is always just gets appended to the current logical line.
316 leaf: the leaf to visit.
318 if leaf
.type in _WHITESPACE_TOKENS
:
320 elif leaf
.type != grammar_token
.COMMENT
or leaf
.value
.strip():
321 # Add non-whitespace tokens and comments that aren't empty.
322 self
._cur
_logical
_line
.AppendToken(
323 format_token
.FormatToken(leaf
, pytree_utils
.NodeName(leaf
)))
326 _BRACKET_MATCH
= {')': '(', '}': '{', ']': '['}
329 def _MatchBrackets(line
):
330 """Visit the node and match the brackets.
332 For every open bracket ('[', '{', or '('), find the associated closing bracket
333 and "match" them up. I.e., save in the token a pointer to its associated open
337 line: (LogicalLine) A logical line.
340 for token
in line
.tokens
:
341 if token
.value
in _OPENING_BRACKETS
:
342 bracket_stack
.append(token
)
343 elif token
.value
in _CLOSING_BRACKETS
:
344 bracket_stack
[-1].matching_bracket
= token
345 token
.matching_bracket
= bracket_stack
[-1]
348 for bracket
in bracket_stack
:
349 if id(pytree_utils
.GetOpeningBracket(token
.node
)) == id(bracket
.node
):
350 bracket
.container_elements
.append(token
)
351 token
.container_opening
= bracket
354 def _IdentifyParameterLists(line
):
355 """Visit the node to create a state for parameter lists.
357 For instance, a parameter is considered an "object" with its first and last
358 token uniquely identifying the object.
361 line: (LogicalLine) A logical line.
365 for tok
in line
.tokens
:
366 # Identify parameter list objects.
367 if subtypes
.FUNC_DEF
in tok
.subtypes
:
368 assert tok
.next_token
.value
== '('
369 func_stack
.append(tok
.next_token
)
372 if func_stack
and tok
.value
== ')':
373 if tok
== func_stack
[-1].matching_bracket
:
377 # Identify parameter objects.
378 if subtypes
.PARAMETER_START
in tok
.subtypes
:
379 param_stack
.append(tok
)
381 # Not "elif", a parameter could be a single token.
382 if param_stack
and subtypes
.PARAMETER_STOP
in tok
.subtypes
:
383 start
= param_stack
.pop()
384 func_stack
[-1].parameters
.append(object_state
.Parameter(start
, tok
))
387 def _AdjustSplitPenalty(line
):
388 """Visit the node and adjust the split penalties if needed.
390 A token shouldn't be split if it's not within a bracket pair. Mark any token
391 that's not within a bracket pair as "unbreakable".
394 line: (LogicalLine) An logical line.
397 for index
, token
in enumerate(line
.tokens
):
398 if index
and not bracket_level
:
399 pytree_utils
.SetNodeAnnotation(token
.node
,
400 pytree_utils
.Annotation
.SPLIT_PENALTY
,
401 split_penalty
.UNBREAKABLE
)
402 if token
.value
in _OPENING_BRACKETS
:
404 elif token
.value
in _CLOSING_BRACKETS
:
408 def _DetermineMustSplitAnnotation(node
):
409 """Enforce a split in the list if the list ends with a comma."""
410 if style
.Get('DISABLE_ENDING_COMMA_HEURISTIC'):
412 if not _ContainsComments(node
):
413 token
= next(node
.parent
.leaves())
414 if token
.value
== '(':
415 if sum(1 for ch
in node
.children
if ch
.type == grammar_token
.COMMA
) < 2:
417 if (not isinstance(node
.children
[-1], pytree
.Leaf
) or
418 node
.children
[-1].value
!= ','):
420 num_children
= len(node
.children
)
422 _SetMustSplitOnFirstLeaf(node
.children
[0])
423 while index
< num_children
- 1:
424 child
= node
.children
[index
]
425 if isinstance(child
, pytree
.Leaf
) and child
.value
== ',':
426 next_child
= node
.children
[index
+ 1]
427 if next_child
.type == grammar_token
.COMMENT
:
429 if index
>= num_children
- 1:
431 _SetMustSplitOnFirstLeaf(node
.children
[index
+ 1])
435 def _ContainsComments(node
):
436 """Return True if the list has a comment in it."""
437 if isinstance(node
, pytree
.Leaf
):
438 return node
.type == grammar_token
.COMMENT
439 for child
in node
.children
:
440 if _ContainsComments(child
):
445 def _SetMustSplitOnFirstLeaf(node
):
446 """Set the "must split" annotation on the first leaf node."""
447 pytree_utils
.SetNodeAnnotation(
448 pytree_utils
.FirstLeafNode(node
), pytree_utils
.Annotation
.MUST_SPLIT
,