]> git.madduck.net Git - etc/vim.git/blob - src/black/linegen.py

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Fail lint CI if the PR doesn't target main (#3477)
[etc/vim.git] / src / black / linegen.py
1 """
2 Generating lines of code.
3 """
4 import sys
5 from dataclasses import dataclass
6 from enum import Enum, auto
7 from functools import partial, wraps
8 from typing import Collection, Iterator, List, Optional, Set, Union, cast
9
10 from black.brackets import (
11     COMMA_PRIORITY,
12     DOT_PRIORITY,
13     get_leaves_inside_matching_brackets,
14     max_delimiter_priority_in_atom,
15 )
16 from black.comments import FMT_OFF, generate_comments, list_comments
17 from black.lines import (
18     Line,
19     append_leaves,
20     can_be_split,
21     can_omit_invisible_parens,
22     is_line_short_enough,
23     line_to_string,
24 )
25 from black.mode import Feature, Mode, Preview
26 from black.nodes import (
27     ASSIGNMENTS,
28     BRACKETS,
29     CLOSING_BRACKETS,
30     OPENING_BRACKETS,
31     RARROW,
32     STANDALONE_COMMENT,
33     STATEMENT,
34     WHITESPACE,
35     Visitor,
36     ensure_visible,
37     is_arith_like,
38     is_atom_with_invisible_parens,
39     is_docstring,
40     is_empty_tuple,
41     is_lpar_token,
42     is_multiline_string,
43     is_name_token,
44     is_one_sequence_between,
45     is_one_tuple,
46     is_rpar_token,
47     is_stub_body,
48     is_stub_suite,
49     is_vararg,
50     is_walrus_assignment,
51     is_yield,
52     syms,
53     wrap_in_parentheses,
54 )
55 from black.numerics import normalize_numeric_literal
56 from black.strings import (
57     fix_docstring,
58     get_string_prefix,
59     normalize_string_prefix,
60     normalize_string_quotes,
61 )
62 from black.trans import (
63     CannotTransform,
64     StringMerger,
65     StringParenStripper,
66     StringParenWrapper,
67     StringSplitter,
68     Transformer,
69     hug_power_op,
70 )
71 from blib2to3.pgen2 import token
72 from blib2to3.pytree import Leaf, Node
73
74 # types
75 LeafID = int
76 LN = Union[Leaf, Node]
77
78
79 class CannotSplit(CannotTransform):
80     """A readable split that fits the allotted line length is impossible."""
81
82
83 # This isn't a dataclass because @dataclass + Generic breaks mypyc.
84 # See also https://github.com/mypyc/mypyc/issues/827.
85 class LineGenerator(Visitor[Line]):
86     """Generates reformatted Line objects.  Empty lines are not emitted.
87
88     Note: destroys the tree it's visiting by mutating prefixes of its leaves
89     in ways that will no longer stringify to valid Python code on the tree.
90     """
91
92     def __init__(self, mode: Mode) -> None:
93         self.mode = mode
94         self.current_line: Line
95         self.__post_init__()
96
97     def line(self, indent: int = 0) -> Iterator[Line]:
98         """Generate a line.
99
100         If the line is empty, only emit if it makes sense.
101         If the line is too long, split it first and then generate.
102
103         If any lines were generated, set up a new current_line.
104         """
105         if not self.current_line:
106             self.current_line.depth += indent
107             return  # Line is empty, don't emit. Creating a new one unnecessary.
108
109         complete_line = self.current_line
110         self.current_line = Line(mode=self.mode, depth=complete_line.depth + indent)
111         yield complete_line
112
113     def visit_default(self, node: LN) -> Iterator[Line]:
114         """Default `visit_*()` implementation. Recurses to children of `node`."""
115         if isinstance(node, Leaf):
116             any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
117             for comment in generate_comments(node, preview=self.mode.preview):
118                 if any_open_brackets:
119                     # any comment within brackets is subject to splitting
120                     self.current_line.append(comment)
121                 elif comment.type == token.COMMENT:
122                     # regular trailing comment
123                     self.current_line.append(comment)
124                     yield from self.line()
125
126                 else:
127                     # regular standalone comment
128                     yield from self.line()
129
130                     self.current_line.append(comment)
131                     yield from self.line()
132
133             normalize_prefix(node, inside_brackets=any_open_brackets)
134             if self.mode.string_normalization and node.type == token.STRING:
135                 node.value = normalize_string_prefix(node.value)
136                 node.value = normalize_string_quotes(node.value)
137             if node.type == token.NUMBER:
138                 normalize_numeric_literal(node)
139             if node.type not in WHITESPACE:
140                 self.current_line.append(node)
141         yield from super().visit_default(node)
142
143     def visit_test(self, node: Node) -> Iterator[Line]:
144         """Visit an `x if y else z` test"""
145
146         if Preview.parenthesize_conditional_expressions in self.mode:
147             already_parenthesized = (
148                 node.prev_sibling and node.prev_sibling.type == token.LPAR
149             )
150
151             if not already_parenthesized:
152                 lpar = Leaf(token.LPAR, "")
153                 rpar = Leaf(token.RPAR, "")
154                 node.insert_child(0, lpar)
155                 node.append_child(rpar)
156
157         yield from self.visit_default(node)
158
159     def visit_INDENT(self, node: Leaf) -> Iterator[Line]:
160         """Increase indentation level, maybe yield a line."""
161         # In blib2to3 INDENT never holds comments.
162         yield from self.line(+1)
163         yield from self.visit_default(node)
164
165     def visit_DEDENT(self, node: Leaf) -> Iterator[Line]:
166         """Decrease indentation level, maybe yield a line."""
167         # The current line might still wait for trailing comments.  At DEDENT time
168         # there won't be any (they would be prefixes on the preceding NEWLINE).
169         # Emit the line then.
170         yield from self.line()
171
172         # While DEDENT has no value, its prefix may contain standalone comments
173         # that belong to the current indentation level.  Get 'em.
174         yield from self.visit_default(node)
175
176         # Finally, emit the dedent.
177         yield from self.line(-1)
178
179     def visit_stmt(
180         self, node: Node, keywords: Set[str], parens: Set[str]
181     ) -> Iterator[Line]:
182         """Visit a statement.
183
184         This implementation is shared for `if`, `while`, `for`, `try`, `except`,
185         `def`, `with`, `class`, `assert`, and assignments.
186
187         The relevant Python language `keywords` for a given statement will be
188         NAME leaves within it. This methods puts those on a separate line.
189
190         `parens` holds a set of string leaf values immediately after which
191         invisible parens should be put.
192         """
193         normalize_invisible_parens(node, parens_after=parens, preview=self.mode.preview)
194         for child in node.children:
195             if is_name_token(child) and child.value in keywords:
196                 yield from self.line()
197
198             yield from self.visit(child)
199
200     def visit_dictsetmaker(self, node: Node) -> Iterator[Line]:
201         if Preview.wrap_long_dict_values_in_parens in self.mode:
202             for i, child in enumerate(node.children):
203                 if i == 0:
204                     continue
205                 if node.children[i - 1].type == token.COLON:
206                     if child.type == syms.atom and child.children[0].type == token.LPAR:
207                         if maybe_make_parens_invisible_in_atom(
208                             child,
209                             parent=node,
210                             remove_brackets_around_comma=False,
211                         ):
212                             wrap_in_parentheses(node, child, visible=False)
213                     else:
214                         wrap_in_parentheses(node, child, visible=False)
215         yield from self.visit_default(node)
216
217     def visit_funcdef(self, node: Node) -> Iterator[Line]:
218         """Visit function definition."""
219         if Preview.annotation_parens not in self.mode:
220             yield from self.visit_stmt(node, keywords={"def"}, parens=set())
221         else:
222             yield from self.line()
223
224             # Remove redundant brackets around return type annotation.
225             is_return_annotation = False
226             for child in node.children:
227                 if child.type == token.RARROW:
228                     is_return_annotation = True
229                 elif is_return_annotation:
230                     if child.type == syms.atom and child.children[0].type == token.LPAR:
231                         if maybe_make_parens_invisible_in_atom(
232                             child,
233                             parent=node,
234                             remove_brackets_around_comma=False,
235                         ):
236                             wrap_in_parentheses(node, child, visible=False)
237                     else:
238                         wrap_in_parentheses(node, child, visible=False)
239                     is_return_annotation = False
240
241             for child in node.children:
242                 yield from self.visit(child)
243
244     def visit_match_case(self, node: Node) -> Iterator[Line]:
245         """Visit either a match or case statement."""
246         normalize_invisible_parens(node, parens_after=set(), preview=self.mode.preview)
247
248         yield from self.line()
249         for child in node.children:
250             yield from self.visit(child)
251
252     def visit_suite(self, node: Node) -> Iterator[Line]:
253         """Visit a suite."""
254         if self.mode.is_pyi and is_stub_suite(node):
255             yield from self.visit(node.children[2])
256         else:
257             yield from self.visit_default(node)
258
259     def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
260         """Visit a statement without nested statements."""
261         prev_type: Optional[int] = None
262         for child in node.children:
263             if (prev_type is None or prev_type == token.SEMI) and is_arith_like(child):
264                 wrap_in_parentheses(node, child, visible=False)
265             prev_type = child.type
266
267         is_suite_like = node.parent and node.parent.type in STATEMENT
268         if is_suite_like:
269             if self.mode.is_pyi and is_stub_body(node):
270                 yield from self.visit_default(node)
271             else:
272                 yield from self.line(+1)
273                 yield from self.visit_default(node)
274                 yield from self.line(-1)
275
276         else:
277             if (
278                 not self.mode.is_pyi
279                 or not node.parent
280                 or not is_stub_suite(node.parent)
281             ):
282                 yield from self.line()
283             yield from self.visit_default(node)
284
285     def visit_async_stmt(self, node: Node) -> Iterator[Line]:
286         """Visit `async def`, `async for`, `async with`."""
287         yield from self.line()
288
289         children = iter(node.children)
290         for child in children:
291             yield from self.visit(child)
292
293             if child.type == token.ASYNC or child.type == STANDALONE_COMMENT:
294                 # STANDALONE_COMMENT happens when `# fmt: skip` is applied on the async
295                 # line.
296                 break
297
298         internal_stmt = next(children)
299         for child in internal_stmt.children:
300             yield from self.visit(child)
301
302     def visit_decorators(self, node: Node) -> Iterator[Line]:
303         """Visit decorators."""
304         for child in node.children:
305             yield from self.line()
306             yield from self.visit(child)
307
308     def visit_power(self, node: Node) -> Iterator[Line]:
309         for idx, leaf in enumerate(node.children[:-1]):
310             next_leaf = node.children[idx + 1]
311
312             if not isinstance(leaf, Leaf):
313                 continue
314
315             value = leaf.value.lower()
316             if (
317                 leaf.type == token.NUMBER
318                 and next_leaf.type == syms.trailer
319                 # Ensure that we are in an attribute trailer
320                 and next_leaf.children[0].type == token.DOT
321                 # It shouldn't wrap hexadecimal, binary and octal literals
322                 and not value.startswith(("0x", "0b", "0o"))
323                 # It shouldn't wrap complex literals
324                 and "j" not in value
325             ):
326                 wrap_in_parentheses(node, leaf)
327
328         if Preview.remove_redundant_parens in self.mode:
329             remove_await_parens(node)
330
331         yield from self.visit_default(node)
332
333     def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]:
334         """Remove a semicolon and put the other statement on a separate line."""
335         yield from self.line()
336
337     def visit_ENDMARKER(self, leaf: Leaf) -> Iterator[Line]:
338         """End of file. Process outstanding comments and end with a newline."""
339         yield from self.visit_default(leaf)
340         yield from self.line()
341
342     def visit_STANDALONE_COMMENT(self, leaf: Leaf) -> Iterator[Line]:
343         if not self.current_line.bracket_tracker.any_open_brackets():
344             yield from self.line()
345         yield from self.visit_default(leaf)
346
347     def visit_factor(self, node: Node) -> Iterator[Line]:
348         """Force parentheses between a unary op and a binary power:
349
350         -2 ** 8 -> -(2 ** 8)
351         """
352         _operator, operand = node.children
353         if (
354             operand.type == syms.power
355             and len(operand.children) == 3
356             and operand.children[1].type == token.DOUBLESTAR
357         ):
358             lpar = Leaf(token.LPAR, "(")
359             rpar = Leaf(token.RPAR, ")")
360             index = operand.remove() or 0
361             node.insert_child(index, Node(syms.atom, [lpar, operand, rpar]))
362         yield from self.visit_default(node)
363
364     def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
365         if is_docstring(leaf) and "\\\n" not in leaf.value:
366             # We're ignoring docstrings with backslash newline escapes because changing
367             # indentation of those changes the AST representation of the code.
368             if Preview.normalize_docstring_quotes_and_prefixes_properly in self.mode:
369                 # There was a bug where --skip-string-normalization wouldn't stop us
370                 # from normalizing docstring prefixes. To maintain stability, we can
371                 # only address this buggy behaviour while the preview style is enabled.
372                 if self.mode.string_normalization:
373                     docstring = normalize_string_prefix(leaf.value)
374                     # visit_default() does handle string normalization for us, but
375                     # since this method acts differently depending on quote style (ex.
376                     # see padding logic below), there's a possibility for unstable
377                     # formatting as visit_default() is called *after*. To avoid a
378                     # situation where this function formats a docstring differently on
379                     # the second pass, normalize it early.
380                     docstring = normalize_string_quotes(docstring)
381                 else:
382                     docstring = leaf.value
383             else:
384                 # ... otherwise, we'll keep the buggy behaviour >.<
385                 docstring = normalize_string_prefix(leaf.value)
386             prefix = get_string_prefix(docstring)
387             docstring = docstring[len(prefix) :]  # Remove the prefix
388             quote_char = docstring[0]
389             # A natural way to remove the outer quotes is to do:
390             #   docstring = docstring.strip(quote_char)
391             # but that breaks on """""x""" (which is '""x').
392             # So we actually need to remove the first character and the next two
393             # characters but only if they are the same as the first.
394             quote_len = 1 if docstring[1] != quote_char else 3
395             docstring = docstring[quote_len:-quote_len]
396             docstring_started_empty = not docstring
397             indent = " " * 4 * self.current_line.depth
398
399             if is_multiline_string(leaf):
400                 docstring = fix_docstring(docstring, indent)
401             else:
402                 docstring = docstring.strip()
403
404             if docstring:
405                 # Add some padding if the docstring starts / ends with a quote mark.
406                 if docstring[0] == quote_char:
407                     docstring = " " + docstring
408                 if docstring[-1] == quote_char:
409                     docstring += " "
410                 if docstring[-1] == "\\":
411                     backslash_count = len(docstring) - len(docstring.rstrip("\\"))
412                     if backslash_count % 2:
413                         # Odd number of tailing backslashes, add some padding to
414                         # avoid escaping the closing string quote.
415                         docstring += " "
416             elif not docstring_started_empty:
417                 docstring = " "
418
419             # We could enforce triple quotes at this point.
420             quote = quote_char * quote_len
421
422             # It's invalid to put closing single-character quotes on a new line.
423             if Preview.long_docstring_quotes_on_newline in self.mode and quote_len == 3:
424                 # We need to find the length of the last line of the docstring
425                 # to find if we can add the closing quotes to the line without
426                 # exceeding the maximum line length.
427                 # If docstring is one line, we don't put the closing quotes on a
428                 # separate line because it looks ugly (#3320).
429                 lines = docstring.splitlines()
430                 last_line_length = len(lines[-1]) if docstring else 0
431
432                 # If adding closing quotes would cause the last line to exceed
433                 # the maximum line length then put a line break before the
434                 # closing quotes
435                 if (
436                     len(lines) > 1
437                     and last_line_length + quote_len > self.mode.line_length
438                 ):
439                     leaf.value = prefix + quote + docstring + "\n" + indent + quote
440                 else:
441                     leaf.value = prefix + quote + docstring + quote
442             else:
443                 leaf.value = prefix + quote + docstring + quote
444
445         yield from self.visit_default(leaf)
446
447     def __post_init__(self) -> None:
448         """You are in a twisty little maze of passages."""
449         self.current_line = Line(mode=self.mode)
450
451         v = self.visit_stmt
452         Ø: Set[str] = set()
453         self.visit_assert_stmt = partial(v, keywords={"assert"}, parens={"assert", ","})
454         self.visit_if_stmt = partial(
455             v, keywords={"if", "else", "elif"}, parens={"if", "elif"}
456         )
457         self.visit_while_stmt = partial(v, keywords={"while", "else"}, parens={"while"})
458         self.visit_for_stmt = partial(v, keywords={"for", "else"}, parens={"for", "in"})
459         self.visit_try_stmt = partial(
460             v, keywords={"try", "except", "else", "finally"}, parens=Ø
461         )
462         if self.mode.preview:
463             self.visit_except_clause = partial(
464                 v, keywords={"except"}, parens={"except"}
465             )
466             self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"})
467         else:
468             self.visit_except_clause = partial(v, keywords={"except"}, parens=Ø)
469             self.visit_with_stmt = partial(v, keywords={"with"}, parens=Ø)
470         self.visit_classdef = partial(v, keywords={"class"}, parens=Ø)
471         self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS)
472         self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"})
473         self.visit_import_from = partial(v, keywords=Ø, parens={"import"})
474         self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"})
475         self.visit_async_funcdef = self.visit_async_stmt
476         self.visit_decorated = self.visit_decorators
477
478         # PEP 634
479         self.visit_match_stmt = self.visit_match_case
480         self.visit_case_block = self.visit_match_case
481
482
483 def transform_line(
484     line: Line, mode: Mode, features: Collection[Feature] = ()
485 ) -> Iterator[Line]:
486     """Transform a `line`, potentially splitting it into many lines.
487
488     They should fit in the allotted `line_length` but might not be able to.
489
490     `features` are syntactical features that may be used in the output.
491     """
492     if line.is_comment:
493         yield line
494         return
495
496     line_str = line_to_string(line)
497
498     ll = mode.line_length
499     sn = mode.string_normalization
500     string_merge = StringMerger(ll, sn)
501     string_paren_strip = StringParenStripper(ll, sn)
502     string_split = StringSplitter(ll, sn)
503     string_paren_wrap = StringParenWrapper(ll, sn)
504
505     transformers: List[Transformer]
506     if (
507         not line.contains_uncollapsable_type_comments()
508         and not line.should_split_rhs
509         and not line.magic_trailing_comma
510         and (
511             is_line_short_enough(line, line_length=mode.line_length, line_str=line_str)
512             or line.contains_unsplittable_type_ignore()
513         )
514         and not (line.inside_brackets and line.contains_standalone_comments())
515     ):
516         # Only apply basic string preprocessing, since lines shouldn't be split here.
517         if Preview.string_processing in mode:
518             transformers = [string_merge, string_paren_strip]
519         else:
520             transformers = []
521     elif line.is_def:
522         transformers = [left_hand_split]
523     else:
524
525         def _rhs(
526             self: object, line: Line, features: Collection[Feature]
527         ) -> Iterator[Line]:
528             """Wraps calls to `right_hand_split`.
529
530             The calls increasingly `omit` right-hand trailers (bracket pairs with
531             content), meaning the trailers get glued together to split on another
532             bracket pair instead.
533             """
534             for omit in generate_trailers_to_omit(line, mode.line_length):
535                 lines = list(
536                     right_hand_split(line, mode.line_length, features, omit=omit)
537                 )
538                 # Note: this check is only able to figure out if the first line of the
539                 # *current* transformation fits in the line length.  This is true only
540                 # for simple cases.  All others require running more transforms via
541                 # `transform_line()`.  This check doesn't know if those would succeed.
542                 if is_line_short_enough(lines[0], line_length=mode.line_length):
543                     yield from lines
544                     return
545
546             # All splits failed, best effort split with no omits.
547             # This mostly happens to multiline strings that are by definition
548             # reported as not fitting a single line, as well as lines that contain
549             # trailing commas (those have to be exploded).
550             yield from right_hand_split(
551                 line, line_length=mode.line_length, features=features
552             )
553
554         # HACK: nested functions (like _rhs) compiled by mypyc don't retain their
555         # __name__ attribute which is needed in `run_transformer` further down.
556         # Unfortunately a nested class breaks mypyc too. So a class must be created
557         # via type ... https://github.com/mypyc/mypyc/issues/884
558         rhs = type("rhs", (), {"__call__": _rhs})()
559
560         if Preview.string_processing in mode:
561             if line.inside_brackets:
562                 transformers = [
563                     string_merge,
564                     string_paren_strip,
565                     string_split,
566                     delimiter_split,
567                     standalone_comment_split,
568                     string_paren_wrap,
569                     rhs,
570                 ]
571             else:
572                 transformers = [
573                     string_merge,
574                     string_paren_strip,
575                     string_split,
576                     string_paren_wrap,
577                     rhs,
578                 ]
579         else:
580             if line.inside_brackets:
581                 transformers = [delimiter_split, standalone_comment_split, rhs]
582             else:
583                 transformers = [rhs]
584     # It's always safe to attempt hugging of power operations and pretty much every line
585     # could match.
586     transformers.append(hug_power_op)
587
588     for transform in transformers:
589         # We are accumulating lines in `result` because we might want to abort
590         # mission and return the original line in the end, or attempt a different
591         # split altogether.
592         try:
593             result = run_transformer(line, transform, mode, features, line_str=line_str)
594         except CannotTransform:
595             continue
596         else:
597             yield from result
598             break
599
600     else:
601         yield line
602
603
604 class _BracketSplitComponent(Enum):
605     head = auto()
606     body = auto()
607     tail = auto()
608
609
610 def left_hand_split(line: Line, _features: Collection[Feature] = ()) -> Iterator[Line]:
611     """Split line into many lines, starting with the first matching bracket pair.
612
613     Note: this usually looks weird, only use this for function definitions.
614     Prefer RHS otherwise.  This is why this function is not symmetrical with
615     :func:`right_hand_split` which also handles optional parentheses.
616     """
617     tail_leaves: List[Leaf] = []
618     body_leaves: List[Leaf] = []
619     head_leaves: List[Leaf] = []
620     current_leaves = head_leaves
621     matching_bracket: Optional[Leaf] = None
622     for leaf in line.leaves:
623         if (
624             current_leaves is body_leaves
625             and leaf.type in CLOSING_BRACKETS
626             and leaf.opening_bracket is matching_bracket
627             and isinstance(matching_bracket, Leaf)
628         ):
629             ensure_visible(leaf)
630             ensure_visible(matching_bracket)
631             current_leaves = tail_leaves if body_leaves else head_leaves
632         current_leaves.append(leaf)
633         if current_leaves is head_leaves:
634             if leaf.type in OPENING_BRACKETS:
635                 matching_bracket = leaf
636                 current_leaves = body_leaves
637     if not matching_bracket:
638         raise CannotSplit("No brackets found")
639
640     head = bracket_split_build_line(
641         head_leaves, line, matching_bracket, component=_BracketSplitComponent.head
642     )
643     body = bracket_split_build_line(
644         body_leaves, line, matching_bracket, component=_BracketSplitComponent.body
645     )
646     tail = bracket_split_build_line(
647         tail_leaves, line, matching_bracket, component=_BracketSplitComponent.tail
648     )
649     bracket_split_succeeded_or_raise(head, body, tail)
650     for result in (head, body, tail):
651         if result:
652             yield result
653
654
655 @dataclass
656 class _RHSResult:
657     """Intermediate split result from a right hand split."""
658
659     head: Line
660     body: Line
661     tail: Line
662     opening_bracket: Leaf
663     closing_bracket: Leaf
664
665
666 def right_hand_split(
667     line: Line,
668     line_length: int,
669     features: Collection[Feature] = (),
670     omit: Collection[LeafID] = (),
671 ) -> Iterator[Line]:
672     """Split line into many lines, starting with the last matching bracket pair.
673
674     If the split was by optional parentheses, attempt splitting without them, too.
675     `omit` is a collection of closing bracket IDs that shouldn't be considered for
676     this split.
677
678     Note: running this function modifies `bracket_depth` on the leaves of `line`.
679     """
680     rhs_result = _first_right_hand_split(line, omit=omit)
681     yield from _maybe_split_omitting_optional_parens(
682         rhs_result, line, line_length, features=features, omit=omit
683     )
684
685
686 def _first_right_hand_split(
687     line: Line,
688     omit: Collection[LeafID] = (),
689 ) -> _RHSResult:
690     """Split the line into head, body, tail starting with the last bracket pair.
691
692     Note: this function should not have side effects. It's relied upon by
693     _maybe_split_omitting_optional_parens to get an opinion whether to prefer
694     splitting on the right side of an assignment statement.
695     """
696     tail_leaves: List[Leaf] = []
697     body_leaves: List[Leaf] = []
698     head_leaves: List[Leaf] = []
699     current_leaves = tail_leaves
700     opening_bracket: Optional[Leaf] = None
701     closing_bracket: Optional[Leaf] = None
702     for leaf in reversed(line.leaves):
703         if current_leaves is body_leaves:
704             if leaf is opening_bracket:
705                 current_leaves = head_leaves if body_leaves else tail_leaves
706         current_leaves.append(leaf)
707         if current_leaves is tail_leaves:
708             if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit:
709                 opening_bracket = leaf.opening_bracket
710                 closing_bracket = leaf
711                 current_leaves = body_leaves
712     if not (opening_bracket and closing_bracket and head_leaves):
713         # If there is no opening or closing_bracket that means the split failed and
714         # all content is in the tail.  Otherwise, if `head_leaves` are empty, it means
715         # the matching `opening_bracket` wasn't available on `line` anymore.
716         raise CannotSplit("No brackets found")
717
718     tail_leaves.reverse()
719     body_leaves.reverse()
720     head_leaves.reverse()
721     head = bracket_split_build_line(
722         head_leaves, line, opening_bracket, component=_BracketSplitComponent.head
723     )
724     body = bracket_split_build_line(
725         body_leaves, line, opening_bracket, component=_BracketSplitComponent.body
726     )
727     tail = bracket_split_build_line(
728         tail_leaves, line, opening_bracket, component=_BracketSplitComponent.tail
729     )
730     bracket_split_succeeded_or_raise(head, body, tail)
731     return _RHSResult(head, body, tail, opening_bracket, closing_bracket)
732
733
734 def _maybe_split_omitting_optional_parens(
735     rhs: _RHSResult,
736     line: Line,
737     line_length: int,
738     features: Collection[Feature] = (),
739     omit: Collection[LeafID] = (),
740 ) -> Iterator[Line]:
741     if (
742         Feature.FORCE_OPTIONAL_PARENTHESES not in features
743         # the opening bracket is an optional paren
744         and rhs.opening_bracket.type == token.LPAR
745         and not rhs.opening_bracket.value
746         # the closing bracket is an optional paren
747         and rhs.closing_bracket.type == token.RPAR
748         and not rhs.closing_bracket.value
749         # it's not an import (optional parens are the only thing we can split on
750         # in this case; attempting a split without them is a waste of time)
751         and not line.is_import
752         # there are no standalone comments in the body
753         and not rhs.body.contains_standalone_comments(0)
754         # and we can actually remove the parens
755         and can_omit_invisible_parens(rhs.body, line_length)
756     ):
757         omit = {id(rhs.closing_bracket), *omit}
758         try:
759             # The _RHSResult Omitting Optional Parens.
760             rhs_oop = _first_right_hand_split(line, omit=omit)
761             if not (
762                 Preview.prefer_splitting_right_hand_side_of_assignments in line.mode
763                 # the split is right after `=`
764                 and len(rhs.head.leaves) >= 2
765                 and rhs.head.leaves[-2].type == token.EQUAL
766                 # the left side of assignement contains brackets
767                 and any(leaf.type in BRACKETS for leaf in rhs.head.leaves[:-1])
768                 # the left side of assignment is short enough (the -1 is for the ending
769                 # optional paren)
770                 and is_line_short_enough(rhs.head, line_length=line_length - 1)
771                 # the left side of assignment won't explode further because of magic
772                 # trailing comma
773                 and rhs.head.magic_trailing_comma is None
774                 # the split by omitting optional parens isn't preferred by some other
775                 # reason
776                 and not _prefer_split_rhs_oop(rhs_oop, line_length=line_length)
777             ):
778                 yield from _maybe_split_omitting_optional_parens(
779                     rhs_oop, line, line_length, features=features, omit=omit
780                 )
781                 return
782
783         except CannotSplit as e:
784             if not (
785                 can_be_split(rhs.body)
786                 or is_line_short_enough(rhs.body, line_length=line_length)
787             ):
788                 raise CannotSplit(
789                     "Splitting failed, body is still too long and can't be split."
790                 ) from e
791
792             elif (
793                 rhs.head.contains_multiline_strings()
794                 or rhs.tail.contains_multiline_strings()
795             ):
796                 raise CannotSplit(
797                     "The current optional pair of parentheses is bound to fail to"
798                     " satisfy the splitting algorithm because the head or the tail"
799                     " contains multiline strings which by definition never fit one"
800                     " line."
801                 ) from e
802
803     ensure_visible(rhs.opening_bracket)
804     ensure_visible(rhs.closing_bracket)
805     for result in (rhs.head, rhs.body, rhs.tail):
806         if result:
807             yield result
808
809
810 def _prefer_split_rhs_oop(rhs_oop: _RHSResult, line_length: int) -> bool:
811     """
812     Returns whether we should prefer the result from a split omitting optional parens.
813     """
814     has_closing_bracket_after_assign = False
815     for leaf in reversed(rhs_oop.head.leaves):
816         if leaf.type == token.EQUAL:
817             break
818         if leaf.type in CLOSING_BRACKETS:
819             has_closing_bracket_after_assign = True
820             break
821     return (
822         # contains matching brackets after the `=` (done by checking there is a
823         # closing bracket)
824         has_closing_bracket_after_assign
825         or (
826             # the split is actually from inside the optional parens (done by checking
827             # the first line still contains the `=`)
828             any(leaf.type == token.EQUAL for leaf in rhs_oop.head.leaves)
829             # the first line is short enough
830             and is_line_short_enough(rhs_oop.head, line_length=line_length)
831         )
832         # contains unsplittable type ignore
833         or rhs_oop.head.contains_unsplittable_type_ignore()
834         or rhs_oop.body.contains_unsplittable_type_ignore()
835         or rhs_oop.tail.contains_unsplittable_type_ignore()
836     )
837
838
839 def bracket_split_succeeded_or_raise(head: Line, body: Line, tail: Line) -> None:
840     """Raise :exc:`CannotSplit` if the last left- or right-hand split failed.
841
842     Do nothing otherwise.
843
844     A left- or right-hand split is based on a pair of brackets. Content before
845     (and including) the opening bracket is left on one line, content inside the
846     brackets is put on a separate line, and finally content starting with and
847     following the closing bracket is put on a separate line.
848
849     Those are called `head`, `body`, and `tail`, respectively. If the split
850     produced the same line (all content in `head`) or ended up with an empty `body`
851     and the `tail` is just the closing bracket, then it's considered failed.
852     """
853     tail_len = len(str(tail).strip())
854     if not body:
855         if tail_len == 0:
856             raise CannotSplit("Splitting brackets produced the same line")
857
858         elif tail_len < 3:
859             raise CannotSplit(
860                 f"Splitting brackets on an empty body to save {tail_len} characters is"
861                 " not worth it"
862             )
863
864
865 def bracket_split_build_line(
866     leaves: List[Leaf],
867     original: Line,
868     opening_bracket: Leaf,
869     *,
870     component: _BracketSplitComponent,
871 ) -> Line:
872     """Return a new line with given `leaves` and respective comments from `original`.
873
874     If it's the head component, brackets will be tracked so trailing commas are
875     respected.
876
877     If it's the body component, the result line is one-indented inside brackets and as
878     such has its first leaf's prefix normalized and a trailing comma added when
879     expected.
880     """
881     result = Line(mode=original.mode, depth=original.depth)
882     if component is _BracketSplitComponent.body:
883         result.inside_brackets = True
884         result.depth += 1
885         if leaves:
886             # Since body is a new indent level, remove spurious leading whitespace.
887             normalize_prefix(leaves[0], inside_brackets=True)
888             # Ensure a trailing comma for imports and standalone function arguments, but
889             # be careful not to add one after any comments or within type annotations.
890             no_commas = (
891                 original.is_def
892                 and opening_bracket.value == "("
893                 and not any(leaf.type == token.COMMA for leaf in leaves)
894                 # In particular, don't add one within a parenthesized return annotation.
895                 # Unfortunately the indicator we're in a return annotation (RARROW) may
896                 # be defined directly in the parent node, the parent of the parent ...
897                 # and so on depending on how complex the return annotation is.
898                 # This isn't perfect and there's some false negatives but they are in
899                 # contexts were a comma is actually fine.
900                 and not any(
901                     node.prev_sibling.type == RARROW
902                     for node in (
903                         leaves[0].parent,
904                         getattr(leaves[0].parent, "parent", None),
905                     )
906                     if isinstance(node, Node) and isinstance(node.prev_sibling, Leaf)
907                 )
908             )
909
910             if original.is_import or no_commas:
911                 for i in range(len(leaves) - 1, -1, -1):
912                     if leaves[i].type == STANDALONE_COMMENT:
913                         continue
914
915                     if leaves[i].type != token.COMMA:
916                         new_comma = Leaf(token.COMMA, ",")
917                         leaves.insert(i + 1, new_comma)
918                     break
919
920     leaves_to_track: Set[LeafID] = set()
921     if (
922         Preview.handle_trailing_commas_in_head in original.mode
923         and component is _BracketSplitComponent.head
924     ):
925         leaves_to_track = get_leaves_inside_matching_brackets(leaves)
926     # Populate the line
927     for leaf in leaves:
928         result.append(
929             leaf,
930             preformatted=True,
931             track_bracket=id(leaf) in leaves_to_track,
932         )
933         for comment_after in original.comments_after(leaf):
934             result.append(comment_after, preformatted=True)
935     if component is _BracketSplitComponent.body and should_split_line(
936         result, opening_bracket
937     ):
938         result.should_split_rhs = True
939     return result
940
941
942 def dont_increase_indentation(split_func: Transformer) -> Transformer:
943     """Normalize prefix of the first leaf in every line returned by `split_func`.
944
945     This is a decorator over relevant split functions.
946     """
947
948     @wraps(split_func)
949     def split_wrapper(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]:
950         for split_line in split_func(line, features):
951             normalize_prefix(split_line.leaves[0], inside_brackets=True)
952             yield split_line
953
954     return split_wrapper
955
956
957 @dont_increase_indentation
958 def delimiter_split(line: Line, features: Collection[Feature] = ()) -> Iterator[Line]:
959     """Split according to delimiters of the highest priority.
960
961     If the appropriate Features are given, the split will add trailing commas
962     also in function signatures and calls that contain `*` and `**`.
963     """
964     try:
965         last_leaf = line.leaves[-1]
966     except IndexError:
967         raise CannotSplit("Line empty") from None
968
969     bt = line.bracket_tracker
970     try:
971         delimiter_priority = bt.max_delimiter_priority(exclude={id(last_leaf)})
972     except ValueError:
973         raise CannotSplit("No delimiters found") from None
974
975     if delimiter_priority == DOT_PRIORITY:
976         if bt.delimiter_count_with_priority(delimiter_priority) == 1:
977             raise CannotSplit("Splitting a single attribute from its owner looks wrong")
978
979     current_line = Line(
980         mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
981     )
982     lowest_depth = sys.maxsize
983     trailing_comma_safe = True
984
985     def append_to_line(leaf: Leaf) -> Iterator[Line]:
986         """Append `leaf` to current line or to new line if appending impossible."""
987         nonlocal current_line
988         try:
989             current_line.append_safe(leaf, preformatted=True)
990         except ValueError:
991             yield current_line
992
993             current_line = Line(
994                 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
995             )
996             current_line.append(leaf)
997
998     for leaf in line.leaves:
999         yield from append_to_line(leaf)
1000
1001         for comment_after in line.comments_after(leaf):
1002             yield from append_to_line(comment_after)
1003
1004         lowest_depth = min(lowest_depth, leaf.bracket_depth)
1005         if leaf.bracket_depth == lowest_depth:
1006             if is_vararg(leaf, within={syms.typedargslist}):
1007                 trailing_comma_safe = (
1008                     trailing_comma_safe and Feature.TRAILING_COMMA_IN_DEF in features
1009                 )
1010             elif is_vararg(leaf, within={syms.arglist, syms.argument}):
1011                 trailing_comma_safe = (
1012                     trailing_comma_safe and Feature.TRAILING_COMMA_IN_CALL in features
1013                 )
1014
1015         leaf_priority = bt.delimiters.get(id(leaf))
1016         if leaf_priority == delimiter_priority:
1017             yield current_line
1018
1019             current_line = Line(
1020                 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1021             )
1022     if current_line:
1023         if (
1024             trailing_comma_safe
1025             and delimiter_priority == COMMA_PRIORITY
1026             and current_line.leaves[-1].type != token.COMMA
1027             and current_line.leaves[-1].type != STANDALONE_COMMENT
1028         ):
1029             new_comma = Leaf(token.COMMA, ",")
1030             current_line.append(new_comma)
1031         yield current_line
1032
1033
1034 @dont_increase_indentation
1035 def standalone_comment_split(
1036     line: Line, features: Collection[Feature] = ()
1037 ) -> Iterator[Line]:
1038     """Split standalone comments from the rest of the line."""
1039     if not line.contains_standalone_comments(0):
1040         raise CannotSplit("Line does not have any standalone comments")
1041
1042     current_line = Line(
1043         mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1044     )
1045
1046     def append_to_line(leaf: Leaf) -> Iterator[Line]:
1047         """Append `leaf` to current line or to new line if appending impossible."""
1048         nonlocal current_line
1049         try:
1050             current_line.append_safe(leaf, preformatted=True)
1051         except ValueError:
1052             yield current_line
1053
1054             current_line = Line(
1055                 line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1056             )
1057             current_line.append(leaf)
1058
1059     for leaf in line.leaves:
1060         yield from append_to_line(leaf)
1061
1062         for comment_after in line.comments_after(leaf):
1063             yield from append_to_line(comment_after)
1064
1065     if current_line:
1066         yield current_line
1067
1068
1069 def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
1070     """Leave existing extra newlines if not `inside_brackets`. Remove everything
1071     else.
1072
1073     Note: don't use backslashes for formatting or you'll lose your voting rights.
1074     """
1075     if not inside_brackets:
1076         spl = leaf.prefix.split("#")
1077         if "\\" not in spl[0]:
1078             nl_count = spl[-1].count("\n")
1079             if len(spl) > 1:
1080                 nl_count -= 1
1081             leaf.prefix = "\n" * nl_count
1082             return
1083
1084     leaf.prefix = ""
1085
1086
1087 def normalize_invisible_parens(
1088     node: Node, parens_after: Set[str], *, preview: bool
1089 ) -> None:
1090     """Make existing optional parentheses invisible or create new ones.
1091
1092     `parens_after` is a set of string leaf values immediately after which parens
1093     should be put.
1094
1095     Standardizes on visible parentheses for single-element tuples, and keeps
1096     existing visible parentheses for other tuples and generator expressions.
1097     """
1098     for pc in list_comments(node.prefix, is_endmarker=False, preview=preview):
1099         if pc.value in FMT_OFF:
1100             # This `node` has a prefix with `# fmt: off`, don't mess with parens.
1101             return
1102     check_lpar = False
1103     for index, child in enumerate(list(node.children)):
1104         # Fixes a bug where invisible parens are not properly stripped from
1105         # assignment statements that contain type annotations.
1106         if isinstance(child, Node) and child.type == syms.annassign:
1107             normalize_invisible_parens(
1108                 child, parens_after=parens_after, preview=preview
1109             )
1110
1111         # Add parentheses around long tuple unpacking in assignments.
1112         if (
1113             index == 0
1114             and isinstance(child, Node)
1115             and child.type == syms.testlist_star_expr
1116         ):
1117             check_lpar = True
1118
1119         if check_lpar:
1120             if (
1121                 preview
1122                 and child.type == syms.atom
1123                 and node.type == syms.for_stmt
1124                 and isinstance(child.prev_sibling, Leaf)
1125                 and child.prev_sibling.type == token.NAME
1126                 and child.prev_sibling.value == "for"
1127             ):
1128                 if maybe_make_parens_invisible_in_atom(
1129                     child,
1130                     parent=node,
1131                     remove_brackets_around_comma=True,
1132                 ):
1133                     wrap_in_parentheses(node, child, visible=False)
1134             elif preview and isinstance(child, Node) and node.type == syms.with_stmt:
1135                 remove_with_parens(child, node)
1136             elif child.type == syms.atom:
1137                 if maybe_make_parens_invisible_in_atom(
1138                     child,
1139                     parent=node,
1140                 ):
1141                     wrap_in_parentheses(node, child, visible=False)
1142             elif is_one_tuple(child):
1143                 wrap_in_parentheses(node, child, visible=True)
1144             elif node.type == syms.import_from:
1145                 # "import from" nodes store parentheses directly as part of
1146                 # the statement
1147                 if is_lpar_token(child):
1148                     assert is_rpar_token(node.children[-1])
1149                     # make parentheses invisible
1150                     child.value = ""
1151                     node.children[-1].value = ""
1152                 elif child.type != token.STAR:
1153                     # insert invisible parentheses
1154                     node.insert_child(index, Leaf(token.LPAR, ""))
1155                     node.append_child(Leaf(token.RPAR, ""))
1156                 break
1157             elif (
1158                 index == 1
1159                 and child.type == token.STAR
1160                 and node.type == syms.except_clause
1161             ):
1162                 # In except* (PEP 654), the star is actually part of
1163                 # of the keyword. So we need to skip the insertion of
1164                 # invisible parentheses to work more precisely.
1165                 continue
1166
1167             elif not (isinstance(child, Leaf) and is_multiline_string(child)):
1168                 wrap_in_parentheses(node, child, visible=False)
1169
1170         comma_check = child.type == token.COMMA if preview else False
1171
1172         check_lpar = isinstance(child, Leaf) and (
1173             child.value in parens_after or comma_check
1174         )
1175
1176
1177 def remove_await_parens(node: Node) -> None:
1178     if node.children[0].type == token.AWAIT and len(node.children) > 1:
1179         if (
1180             node.children[1].type == syms.atom
1181             and node.children[1].children[0].type == token.LPAR
1182         ):
1183             if maybe_make_parens_invisible_in_atom(
1184                 node.children[1],
1185                 parent=node,
1186                 remove_brackets_around_comma=True,
1187             ):
1188                 wrap_in_parentheses(node, node.children[1], visible=False)
1189
1190             # Since await is an expression we shouldn't remove
1191             # brackets in cases where this would change
1192             # the AST due to operator precedence.
1193             # Therefore we only aim to remove brackets around
1194             # power nodes that aren't also await expressions themselves.
1195             # https://peps.python.org/pep-0492/#updated-operator-precedence-table
1196             # N.B. We've still removed any redundant nested brackets though :)
1197             opening_bracket = cast(Leaf, node.children[1].children[0])
1198             closing_bracket = cast(Leaf, node.children[1].children[-1])
1199             bracket_contents = cast(Node, node.children[1].children[1])
1200             if bracket_contents.type != syms.power:
1201                 ensure_visible(opening_bracket)
1202                 ensure_visible(closing_bracket)
1203             elif (
1204                 bracket_contents.type == syms.power
1205                 and bracket_contents.children[0].type == token.AWAIT
1206             ):
1207                 ensure_visible(opening_bracket)
1208                 ensure_visible(closing_bracket)
1209                 # If we are in a nested await then recurse down.
1210                 remove_await_parens(bracket_contents)
1211
1212
1213 def remove_with_parens(node: Node, parent: Node) -> None:
1214     """Recursively hide optional parens in `with` statements."""
1215     # Removing all unnecessary parentheses in with statements in one pass is a tad
1216     # complex as different variations of bracketed statements result in pretty
1217     # different parse trees:
1218     #
1219     # with (open("file")) as f:                       # this is an asexpr_test
1220     #     ...
1221     #
1222     # with (open("file") as f):                       # this is an atom containing an
1223     #     ...                                         # asexpr_test
1224     #
1225     # with (open("file")) as f, (open("file")) as f:  # this is asexpr_test, COMMA,
1226     #     ...                                         # asexpr_test
1227     #
1228     # with (open("file") as f, open("file") as f):    # an atom containing a
1229     #     ...                                         # testlist_gexp which then
1230     #                                                 # contains multiple asexpr_test(s)
1231     if node.type == syms.atom:
1232         if maybe_make_parens_invisible_in_atom(
1233             node,
1234             parent=parent,
1235             remove_brackets_around_comma=True,
1236         ):
1237             wrap_in_parentheses(parent, node, visible=False)
1238         if isinstance(node.children[1], Node):
1239             remove_with_parens(node.children[1], node)
1240     elif node.type == syms.testlist_gexp:
1241         for child in node.children:
1242             if isinstance(child, Node):
1243                 remove_with_parens(child, node)
1244     elif node.type == syms.asexpr_test and not any(
1245         leaf.type == token.COLONEQUAL for leaf in node.leaves()
1246     ):
1247         if maybe_make_parens_invisible_in_atom(
1248             node.children[0],
1249             parent=node,
1250             remove_brackets_around_comma=True,
1251         ):
1252             wrap_in_parentheses(node, node.children[0], visible=False)
1253
1254
1255 def maybe_make_parens_invisible_in_atom(
1256     node: LN,
1257     parent: LN,
1258     remove_brackets_around_comma: bool = False,
1259 ) -> bool:
1260     """If it's safe, make the parens in the atom `node` invisible, recursively.
1261     Additionally, remove repeated, adjacent invisible parens from the atom `node`
1262     as they are redundant.
1263
1264     Returns whether the node should itself be wrapped in invisible parentheses.
1265     """
1266     if (
1267         node.type != syms.atom
1268         or is_empty_tuple(node)
1269         or is_one_tuple(node)
1270         or (is_yield(node) and parent.type != syms.expr_stmt)
1271         or (
1272             # This condition tries to prevent removing non-optional brackets
1273             # around a tuple, however, can be a bit overzealous so we provide
1274             # and option to skip this check for `for` and `with` statements.
1275             not remove_brackets_around_comma
1276             and max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY
1277         )
1278     ):
1279         return False
1280
1281     if is_walrus_assignment(node):
1282         if parent.type in [
1283             syms.annassign,
1284             syms.expr_stmt,
1285             syms.assert_stmt,
1286             syms.return_stmt,
1287             syms.except_clause,
1288             syms.funcdef,
1289             # these ones aren't useful to end users, but they do please fuzzers
1290             syms.for_stmt,
1291             syms.del_stmt,
1292         ]:
1293             return False
1294
1295     first = node.children[0]
1296     last = node.children[-1]
1297     if is_lpar_token(first) and is_rpar_token(last):
1298         middle = node.children[1]
1299         # make parentheses invisible
1300         first.value = ""
1301         last.value = ""
1302         maybe_make_parens_invisible_in_atom(
1303             middle,
1304             parent=parent,
1305             remove_brackets_around_comma=remove_brackets_around_comma,
1306         )
1307
1308         if is_atom_with_invisible_parens(middle):
1309             # Strip the invisible parens from `middle` by replacing
1310             # it with the child in-between the invisible parens
1311             middle.replace(middle.children[1])
1312
1313         return False
1314
1315     return True
1316
1317
1318 def should_split_line(line: Line, opening_bracket: Leaf) -> bool:
1319     """Should `line` be immediately split with `delimiter_split()` after RHS?"""
1320
1321     if not (opening_bracket.parent and opening_bracket.value in "[{("):
1322         return False
1323
1324     # We're essentially checking if the body is delimited by commas and there's more
1325     # than one of them (we're excluding the trailing comma and if the delimiter priority
1326     # is still commas, that means there's more).
1327     exclude = set()
1328     trailing_comma = False
1329     try:
1330         last_leaf = line.leaves[-1]
1331         if last_leaf.type == token.COMMA:
1332             trailing_comma = True
1333             exclude.add(id(last_leaf))
1334         max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude)
1335     except (IndexError, ValueError):
1336         return False
1337
1338     return max_priority == COMMA_PRIORITY and (
1339         (line.mode.magic_trailing_comma and trailing_comma)
1340         # always explode imports
1341         or opening_bracket.parent.type in {syms.atom, syms.import_from}
1342     )
1343
1344
1345 def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[LeafID]]:
1346     """Generate sets of closing bracket IDs that should be omitted in a RHS.
1347
1348     Brackets can be omitted if the entire trailer up to and including
1349     a preceding closing bracket fits in one line.
1350
1351     Yielded sets are cumulative (contain results of previous yields, too).  First
1352     set is empty, unless the line should explode, in which case bracket pairs until
1353     the one that needs to explode are omitted.
1354     """
1355
1356     omit: Set[LeafID] = set()
1357     if not line.magic_trailing_comma:
1358         yield omit
1359
1360     length = 4 * line.depth
1361     opening_bracket: Optional[Leaf] = None
1362     closing_bracket: Optional[Leaf] = None
1363     inner_brackets: Set[LeafID] = set()
1364     for index, leaf, leaf_length in line.enumerate_with_length(reversed=True):
1365         length += leaf_length
1366         if length > line_length:
1367             break
1368
1369         has_inline_comment = leaf_length > len(leaf.value) + len(leaf.prefix)
1370         if leaf.type == STANDALONE_COMMENT or has_inline_comment:
1371             break
1372
1373         if opening_bracket:
1374             if leaf is opening_bracket:
1375                 opening_bracket = None
1376             elif leaf.type in CLOSING_BRACKETS:
1377                 prev = line.leaves[index - 1] if index > 0 else None
1378                 if (
1379                     prev
1380                     and prev.type == token.COMMA
1381                     and leaf.opening_bracket is not None
1382                     and not is_one_sequence_between(
1383                         leaf.opening_bracket, leaf, line.leaves
1384                     )
1385                 ):
1386                     # Never omit bracket pairs with trailing commas.
1387                     # We need to explode on those.
1388                     break
1389
1390                 inner_brackets.add(id(leaf))
1391         elif leaf.type in CLOSING_BRACKETS:
1392             prev = line.leaves[index - 1] if index > 0 else None
1393             if prev and prev.type in OPENING_BRACKETS:
1394                 # Empty brackets would fail a split so treat them as "inner"
1395                 # brackets (e.g. only add them to the `omit` set if another
1396                 # pair of brackets was good enough.
1397                 inner_brackets.add(id(leaf))
1398                 continue
1399
1400             if closing_bracket:
1401                 omit.add(id(closing_bracket))
1402                 omit.update(inner_brackets)
1403                 inner_brackets.clear()
1404                 yield omit
1405
1406             if (
1407                 prev
1408                 and prev.type == token.COMMA
1409                 and leaf.opening_bracket is not None
1410                 and not is_one_sequence_between(leaf.opening_bracket, leaf, line.leaves)
1411             ):
1412                 # Never omit bracket pairs with trailing commas.
1413                 # We need to explode on those.
1414                 break
1415
1416             if leaf.value:
1417                 opening_bracket = leaf.opening_bracket
1418                 closing_bracket = leaf
1419
1420
1421 def run_transformer(
1422     line: Line,
1423     transform: Transformer,
1424     mode: Mode,
1425     features: Collection[Feature],
1426     *,
1427     line_str: str = "",
1428 ) -> List[Line]:
1429     if not line_str:
1430         line_str = line_to_string(line)
1431     result: List[Line] = []
1432     for transformed_line in transform(line, features):
1433         if str(transformed_line).strip("\n") == line_str:
1434             raise CannotTransform("Line transformer returned an unchanged result")
1435
1436         result.extend(transform_line(transformed_line, mode=mode, features=features))
1437
1438     features_set = set(features)
1439     if (
1440         Feature.FORCE_OPTIONAL_PARENTHESES in features_set
1441         or transform.__class__.__name__ != "rhs"
1442         or not line.bracket_tracker.invisible
1443         or any(bracket.value for bracket in line.bracket_tracker.invisible)
1444         or line.contains_multiline_strings()
1445         or result[0].contains_uncollapsable_type_comments()
1446         or result[0].contains_unsplittable_type_ignore()
1447         or is_line_short_enough(result[0], line_length=mode.line_length)
1448         # If any leaves have no parents (which _can_ occur since
1449         # `transform(line)` potentially destroys the line's underlying node
1450         # structure), then we can't proceed. Doing so would cause the below
1451         # call to `append_leaves()` to fail.
1452         or any(leaf.parent is None for leaf in line.leaves)
1453     ):
1454         return result
1455
1456     line_copy = line.clone()
1457     append_leaves(line_copy, line, line.leaves)
1458     features_fop = features_set | {Feature.FORCE_OPTIONAL_PARENTHESES}
1459     second_opinion = run_transformer(
1460         line_copy, transform, mode, features_fop, line_str=line_str
1461     )
1462     if all(
1463         is_line_short_enough(ln, line_length=mode.line_length) for ln in second_opinion
1464     ):
1465         result = second_opinion
1466     return result