]> 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:

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