Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/black/linegen.py: 10%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Generating lines of code.
3"""
5import re
6import sys
7from collections.abc import Collection, Iterable, Iterator
8from dataclasses import replace
9from enum import Enum, auto
10from functools import partial, wraps
11from typing import Final, Union, cast
13from black.brackets import (
14 COMMA_PRIORITY,
15 COMPARATOR_PRIORITY,
16 DOT_PRIORITY,
17 LOGIC_PRIORITY,
18 STRING_PRIORITY,
19 get_leaves_inside_matching_brackets,
20 max_delimiter_priority_in_atom,
21)
22from black.comments import (
23 FMT_OFF,
24 FMT_ON,
25 contains_fmt_directive,
26 generate_comments,
27 list_comments,
28)
29from black.lines import (
30 Line,
31 RHSResult,
32 append_leaves,
33 can_be_split,
34 can_omit_invisible_parens,
35 is_line_short_enough,
36 is_symmetric_collection_binop,
37 line_to_string,
38)
39from black.mode import Feature, Mode, Preview
40from black.nodes import (
41 ASSIGNMENTS,
42 BRACKETS,
43 CLOSING_BRACKETS,
44 OPENING_BRACKETS,
45 STANDALONE_COMMENT,
46 STATEMENT,
47 WHITESPACE,
48 Visitor,
49 ensure_visible,
50 first_leaf_of,
51 fstring_tstring_to_string,
52 get_annotation_type,
53 has_sibling_with_type,
54 is_arith_like,
55 is_async_stmt_or_funcdef,
56 is_atom_with_invisible_parens,
57 is_docstring,
58 is_empty_tuple,
59 is_generator,
60 is_list,
61 is_lpar_token,
62 is_multiline_string,
63 is_name_token,
64 is_one_sequence_between,
65 is_one_tuple,
66 is_parent_function_or_class,
67 is_part_of_annotation,
68 is_rpar_token,
69 is_stub_body,
70 is_stub_suite,
71 is_tuple,
72 is_tuple_containing_star,
73 is_tuple_containing_walrus,
74 is_type_ignore_comment,
75 is_type_ignore_comment_string,
76 is_vararg,
77 is_walrus_assignment,
78 is_yield,
79 syms,
80 wrap_in_parentheses,
81)
82from black.numerics import normalize_numeric_literal
83from black.strings import (
84 fix_multiline_docstring,
85 get_string_prefix,
86 normalize_fstring_unicode_escape_sequences,
87 normalize_string_prefix,
88 normalize_string_quotes,
89 normalize_unicode_escape_sequences,
90 str_width,
91)
92from black.trans import (
93 CannotTransform,
94 StringMerger,
95 StringParenStripper,
96 StringParenWrapper,
97 StringSplitter,
98 Transformer,
99 hug_power_op,
100)
101from blib2to3.pgen2 import token
102from blib2to3.pytree import Leaf, Node
104# types
105LeafID = int
106LN = Union[Leaf, Node]
109class CannotSplit(CannotTransform):
110 """A readable split that fits the allotted line length is impossible."""
113# This isn't a dataclass because @dataclass + Generic breaks mypyc.
114# See also https://github.com/mypyc/mypyc/issues/827.
115class LineGenerator(Visitor[Line]):
116 """Generates reformatted Line objects. Empty lines are not emitted.
118 Note: destroys the tree it's visiting by mutating prefixes of its leaves
119 in ways that will no longer stringify to valid Python code on the tree.
120 """
122 def __init__(self, mode: Mode, features: Collection[Feature]) -> None:
123 self.mode = mode
124 self.features = features
125 self.current_line: Line
126 self.__post_init__()
128 def line(self, indent: int = 0) -> Iterator[Line]:
129 """Generate a line.
131 If the line is empty, only emit if it makes sense.
132 If the line is too long, split it first and then generate.
134 If any lines were generated, set up a new current_line.
135 """
136 if not self.current_line:
137 self.current_line.depth += indent
138 return # Line is empty, don't emit. Creating a new one unnecessary.
140 if len(self.current_line.leaves) == 1 and is_async_stmt_or_funcdef(
141 self.current_line.leaves[0]
142 ):
143 # Special case for async def/for/with statements. `visit_async_stmt`
144 # adds an `ASYNC` leaf then visits the child def/for/with statement
145 # nodes. Line yields from those nodes shouldn't treat the former
146 # `ASYNC` leaf as a complete line.
147 return
149 complete_line = self.current_line
150 self.current_line = Line(mode=self.mode, depth=complete_line.depth + indent)
151 yield complete_line
153 def visit_default(self, node: LN) -> Iterator[Line]:
154 """Default `visit_*()` implementation. Recurses to children of `node`."""
155 if isinstance(node, Leaf):
156 any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
157 for comment in generate_comments(
158 node, mode=self.mode, block_depth=self.current_line.depth
159 ):
160 if any_open_brackets:
161 # any comment within brackets is subject to splitting
162 self.current_line.append(comment)
163 elif comment.type == token.COMMENT:
164 # regular trailing comment
165 self.current_line.append(comment)
166 yield from self.line()
168 else:
169 # regular standalone comment
170 yield from self.line()
172 self.current_line.append(comment)
173 yield from self.line()
175 if any_open_brackets:
176 node.prefix = ""
177 if node.type not in WHITESPACE:
178 self.current_line.append(node)
179 yield from super().visit_default(node)
181 def visit_test(self, node: Node) -> Iterator[Line]:
182 """Visit an `x if y else z` test"""
184 already_parenthesized = (
185 node.prev_sibling and node.prev_sibling.type == token.LPAR
186 )
187 # `# fmt: skip` on an opening bracket can hide the start of the expression,
188 # as in `x = ( # fmt: skip` ... `) if c else d`. Outside of brackets, the
189 # rest has to stay on the line of the hidden closing bracket, so it can't be
190 # wrapped in parentheses.
191 # The `if` part can itself be hidden by another `# fmt: skip`. The hidden
192 # code is then replaced by a STANDALONE_COMMENT at the index of its first
193 # node in the same parent, so a comment in `node.children[0]` hid this
194 # node's own `if` or `else`, and `fmt_pass_converted_first_leaf` is that
195 # keyword (detached from the tree, only its value is read).
196 first = node.children[0]
197 if isinstance(first, Leaf) and first.fmt_pass_converted_first_leaf:
198 first = first.fmt_pass_converted_first_leaf
199 follows_fmt_skip = (
200 isinstance(first, Leaf)
201 and first.value in {"if", "else"}
202 and not self.current_line.bracket_tracker.any_open_brackets()
203 )
205 if not already_parenthesized and not follows_fmt_skip:
206 # Similar to logic in wrap_in_parentheses
207 lpar = Leaf(token.LPAR, "")
208 rpar = Leaf(token.RPAR, "")
209 prefix = node.prefix
210 node.prefix = ""
211 lpar.prefix = prefix
212 node.insert_child(0, lpar)
213 node.append_child(rpar)
215 yield from self.visit_default(node)
217 def visit_comparison(self, node: Node) -> Iterator[Line]:
218 if Preview.remove_redundant_comparison_parentheses in self.mode:
219 _normalize_comparison_operands(node, mode=self.mode, features=self.features)
220 yield from self.visit_default(node)
222 def visit_INDENT(self, node: Leaf) -> Iterator[Line]:
223 """Increase indentation level, maybe yield a line."""
224 # In blib2to3 INDENT never holds comments.
225 yield from self.line(+1)
226 yield from self.visit_default(node)
228 def visit_DEDENT(self, node: Leaf) -> Iterator[Line]:
229 """Decrease indentation level, maybe yield a line."""
230 # The current line might still wait for trailing comments. At DEDENT time
231 # there won't be any (they would be prefixes on the preceding NEWLINE).
232 # Emit the line then.
233 yield from self.line()
235 # While DEDENT has no value, its prefix may contain standalone comments
236 # that belong to the current indentation level. Get 'em.
237 yield from self.visit_default(node)
239 # Finally, emit the dedent.
240 yield from self.line(-1)
242 def visit_stmt(
243 self, node: Node, keywords: set[str], parens: set[str]
244 ) -> Iterator[Line]:
245 """Visit a statement.
247 This implementation is shared for `if`, `while`, `for`, `try`, `except`,
248 `def`, `with`, `class`, `assert`, and assignments.
250 The relevant Python language `keywords` for a given statement
251 appear as NAME leaves within it. This method puts those on a
252 separate line.
254 `parens` holds a set of string leaf values immediately after which
255 invisible parens should be put.
256 """
257 normalize_invisible_parens(
258 node, parens_after=parens, mode=self.mode, features=self.features
259 )
260 for child in node.children:
261 if is_name_token(child) and child.value in keywords:
262 yield from self.line()
264 yield from self.visit(child)
266 def visit_dictsetmaker(self, node: Node) -> Iterator[Line]:
267 if Preview.wrap_long_dict_values_in_parens in self.mode:
268 for i, child in enumerate(node.children):
269 if i == 0:
270 continue
271 if node.children[i - 1].type == token.COLON:
272 if (
273 child.type == syms.atom
274 and child.children[0].type in OPENING_BRACKETS
275 and not is_walrus_assignment(child)
276 ):
277 maybe_make_parens_invisible_in_atom(
278 child,
279 parent=node,
280 mode=self.mode,
281 features=self.features,
282 remove_brackets_around_comma=False,
283 )
284 else:
285 wrap_in_parentheses(node, child, visible=False, index=i)
286 yield from self.visit_default(node)
288 def visit_funcdef(self, node: Node) -> Iterator[Line]:
289 """Visit function definition."""
290 yield from self.line()
292 # Remove redundant brackets around return type annotation.
293 is_return_annotation = False
294 for child in node.children:
295 if child.type == token.RARROW:
296 is_return_annotation = True
297 elif is_return_annotation:
298 if child.type == syms.atom and child.children[0].type == token.LPAR:
299 if maybe_make_parens_invisible_in_atom(
300 child,
301 parent=node,
302 mode=self.mode,
303 features=self.features,
304 remove_brackets_around_comma=False,
305 ):
306 wrap_in_parentheses(node, child, visible=False)
307 else:
308 wrap_in_parentheses(node, child, visible=False)
309 is_return_annotation = False
311 for child in node.children:
312 yield from self.visit(child)
314 def visit_match_case(self, node: Node) -> Iterator[Line]:
315 """Visit either a match or case statement."""
316 normalize_invisible_parens(
317 node, parens_after=set(), mode=self.mode, features=self.features
318 )
320 yield from self.line()
321 for child in node.children:
322 yield from self.visit(child)
324 def visit_suite(self, node: Node) -> Iterator[Line]:
325 """Visit a suite."""
326 if is_stub_suite(node):
327 yield from self.visit(node.children[2])
328 else:
329 yield from self.visit_default(node)
331 def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
332 """Visit a statement without nested statements."""
333 prev_type: int | None = None
334 for i, child in enumerate(node.children):
335 if prev_type is None or prev_type == token.SEMI:
336 if is_arith_like(child):
337 wrap_in_parentheses(node, child, visible=False, index=i)
338 elif (
339 Preview.remove_redundant_top_level_parentheses in self.mode
340 and child.type == syms.atom
341 and not is_walrus_assignment(child)
342 and not _is_implicitly_concatenated_string(child)
343 ):
344 maybe_make_parens_invisible_in_atom(
345 child,
346 parent=node,
347 mode=self.mode,
348 features=self.features,
349 )
350 prev_type = child.type
352 if node.parent and node.parent.type in STATEMENT:
353 if is_parent_function_or_class(node) and is_stub_body(node):
354 yield from self.visit_default(node)
355 else:
356 yield from self.line(+1)
357 yield from self.visit_default(node)
358 yield from self.line(-1)
360 else:
361 if node.parent and is_stub_suite(node.parent):
362 node.prefix = ""
363 yield from self.visit_default(node)
364 return
365 yield from self.line()
366 yield from self.visit_default(node)
368 def visit_async_stmt(self, node: Node) -> Iterator[Line]:
369 """Visit `async def`, `async for`, `async with`."""
370 yield from self.line()
372 children = iter(node.children)
373 for child in children:
374 yield from self.visit(child)
376 if child.type == token.ASYNC or child.type == STANDALONE_COMMENT:
377 # STANDALONE_COMMENT happens when `# fmt: skip` is applied on the async
378 # line.
379 break
381 internal_stmt = next(children)
382 yield from self.visit(internal_stmt)
384 def visit_decorators(self, node: Node) -> Iterator[Line]:
385 """Visit decorators."""
386 for child in node.children:
387 yield from self.line()
388 yield from self.visit(child)
390 def visit_power(self, node: Node) -> Iterator[Line]:
391 for idx, leaf in enumerate(node.children[:-1]):
392 next_leaf = node.children[idx + 1]
394 if not isinstance(leaf, Leaf):
395 continue
397 value = leaf.value.lower()
398 if (
399 leaf.type == token.NUMBER
400 and next_leaf.type == syms.trailer
401 # Ensure that we are in an attribute trailer
402 and next_leaf.children[0].type == token.DOT
403 # It shouldn't wrap hexadecimal, binary and octal literals
404 and not value.startswith(("0x", "0b", "0o"))
405 # It shouldn't wrap complex literals
406 and "j" not in value
407 ):
408 wrap_in_parentheses(node, leaf)
410 remove_generator_parens = (
411 Preview.remove_redundant_generator_parentheses in self.mode
412 )
413 remove_subscript_parens = (
414 Preview.remove_redundant_subscript_parentheses in self.mode
415 )
416 if remove_generator_parens or remove_subscript_parens:
417 for child in node.children:
418 if child.type == syms.trailer and len(child.children) == 3:
419 if (
420 remove_generator_parens
421 and is_lpar_token(child.children[0])
422 and (
423 is_generator(child.children[1])
424 or _has_redundant_generator_parentheses(child.children[1])
425 )
426 and is_rpar_token(child.children[2])
427 ):
428 maybe_make_parens_invisible_in_atom(
429 child.children[1],
430 parent=child,
431 mode=self.mode,
432 features=self.features,
433 remove_generator_parens=True,
434 )
435 atom = child.children[1]
436 if is_atom_with_invisible_parens(atom) and not any(
437 "#" in leaf.prefix for leaf in atom.leaves()
438 ):
439 # Drop the now invisible parens so the generator splits
440 # the same way as one passed without parentheses.
441 atom.replace(atom.children[1])
442 elif (
443 remove_subscript_parens
444 and child.children[0].type == token.LSQB
445 and child.children[2].type == token.RSQB
446 and child.children[1].type == syms.atom
447 ):
448 atom = child.children[1]
449 if not is_empty_tuple(atom) and not is_generator(atom):
450 maybe_make_parens_invisible_in_atom(
451 atom,
452 parent=child,
453 mode=self.mode,
454 features=self.features,
455 remove_brackets_around_comma=True,
456 is_subscript=True,
457 )
458 if is_atom_with_invisible_parens(atom):
459 inner = atom.children[1]
460 if inner.type == syms.testlist_gexp:
461 inner.type = syms.subscriptlist
462 first_paren = atom.children[0]
463 last_paren = atom.children[-1]
464 atom.replace(inner)
465 if first_paren.prefix.strip():
466 first_inner = first_leaf_of(inner)
467 if first_inner:
468 first_inner.prefix = (
469 first_paren.prefix + first_inner.prefix
470 )
471 if last_paren.prefix.strip():
472 child.children[-1].prefix = (
473 last_paren.prefix + child.children[-1].prefix
474 )
476 remove_await_parens(node, mode=self.mode, features=self.features)
478 yield from self.visit_default(node)
480 def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]:
481 """Remove a semicolon and put the other statement on a separate line."""
482 yield from self.line()
484 def visit_ENDMARKER(self, leaf: Leaf) -> Iterator[Line]:
485 """End of file. Process outstanding comments and end with a newline."""
486 yield from self.visit_default(leaf)
487 yield from self.line()
489 def visit_STANDALONE_COMMENT(self, leaf: Leaf) -> Iterator[Line]:
490 any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
491 # Code hidden by a second `# fmt: skip` in the same statement stays on the
492 # line where the previous hidden code ends, with its original spacing.
493 continues_fmt_skip_line = False
494 original_prefix = leaf.prefix
495 if not any_open_brackets:
496 continues_fmt_skip_line = _continues_fmt_skip_line(self.current_line, leaf)
497 if not continues_fmt_skip_line:
498 yield from self.line()
499 # STANDALONE_COMMENT nodes created by our special handling in
500 # normalize_fmt_off for comment-only blocks have fmt:off as the first
501 # line and fmt:on as the last line (each directive on its own line,
502 # not embedded in other text). These should be appended directly
503 # without calling visit_default, which would process their prefix and
504 # lose indentation. Normal STANDALONE_COMMENT nodes go through
505 # visit_default.
506 value = leaf.value
507 lines = value.splitlines()
508 is_fmt_off_block = (
509 len(lines) >= 2
510 and contains_fmt_directive(lines[0], FMT_OFF)
511 and contains_fmt_directive(lines[-1], FMT_ON)
512 )
513 if is_fmt_off_block:
514 is_after_invisible_lpar = (
515 leaf.fmt_pass_converted_first_leaf is None
516 and len(self.current_line.leaves) == 1
517 and self.current_line.leaves[0].type == token.LPAR
518 and not self.current_line.leaves[0].value
519 )
520 # This is a fmt:off/on block from normalize_fmt_off - we still need
521 # to process any prefix comments (like markdown comments) but append
522 # the fmt block itself directly to preserve its formatting
524 # Only process prefix comments if there actually is a prefix with comments
525 if leaf.prefix and any(
526 line.strip().startswith("#")
527 and not contains_fmt_directive(line.strip())
528 for line in leaf.prefix.split("\n")
529 ):
530 for comment in generate_comments(leaf, mode=self.mode):
531 yield from self.line()
532 self.current_line.append(comment)
533 yield from self.line()
534 # Clear the prefix since we've processed it as comments above
535 leaf.prefix = ""
537 self.current_line.append(leaf)
538 if not any_open_brackets or is_after_invisible_lpar:
539 yield from self.line()
540 else:
541 # Normal standalone comment - process through visit_default
542 yield from self.visit_default(leaf)
543 if continues_fmt_skip_line:
544 leaf.prefix = original_prefix
546 def visit_factor(self, node: Node) -> Iterator[Line]:
547 """Force parentheses between a unary op and a binary power:
549 -2 ** 8 -> -(2 ** 8)
550 """
551 # The operator is gone when a `# fmt: skip` on the operand's opening bracket
552 # hides it and the operand goes on after the closing bracket, as in
553 # `x = -( # fmt: skip` ... `).y`.
554 operand = node.children[-1]
555 if (
556 len(node.children) == 2
557 and operand.type == syms.power
558 and len(operand.children) == 3
559 and operand.children[1].type == token.DOUBLESTAR
560 ):
561 lpar = Leaf(token.LPAR, "(")
562 rpar = Leaf(token.RPAR, ")")
563 index = operand.remove() or 0
564 node.insert_child(index, Node(syms.atom, [lpar, operand, rpar]))
565 yield from self.visit_default(node)
567 def visit_tname(self, node: Node) -> Iterator[Line]:
568 """
569 Add potential parentheses around types in function parameter lists to be made
570 into real parentheses in case the type hint is too long to fit on a line
571 Examples:
572 def foo(a: int, b: float = 7): ...
574 ->
576 def foo(a: (int), b: (float) = 7): ...
577 """
578 if len(node.children) == 3 and maybe_make_parens_invisible_in_atom(
579 node.children[2], parent=node, mode=self.mode, features=self.features
580 ):
581 wrap_in_parentheses(node, node.children[2], visible=False)
583 yield from self.visit_default(node)
585 def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
586 normalize_unicode_escape_sequences(leaf)
588 if is_docstring(leaf) and not re.search(r"\\\s*\n", leaf.value):
589 # We're ignoring docstrings with backslash newline escapes because changing
590 # indentation of those changes the AST representation of the code.
591 if self.mode.string_normalization:
592 docstring = normalize_string_prefix(leaf.value, self.mode)
593 # We handle string normalization at the end of this method, but since
594 # what we do right now acts differently depending on quote style (ex.
595 # see padding logic below), there's a possibility for unstable
596 # formatting. To avoid a situation where this function formats a
597 # docstring differently on the second pass, normalize it early.
598 docstring = normalize_string_quotes(docstring)
599 else:
600 docstring = leaf.value
601 prefix = get_string_prefix(docstring)
602 docstring = docstring[len(prefix) :] # Remove the prefix
603 quote_char = docstring[0]
604 # A natural way to remove the outer quotes is to do:
605 # docstring = docstring.strip(quote_char)
606 # but that breaks on """""x""" (which is '""x').
607 # So we actually need to remove the first character and the next two
608 # characters but only if they are the same as the first.
609 quote_len = 1 if docstring[1] != quote_char else 3
610 docstring = docstring[quote_len:-quote_len]
611 docstring_started_empty = not docstring
612 indent = " " * 4 * self.current_line.depth
614 if is_multiline_string(leaf):
615 docstring = fix_multiline_docstring(docstring, indent)
616 else:
617 docstring = docstring.strip()
619 has_trailing_backslash = False
620 if docstring:
621 # Add some padding if the docstring starts / ends with a quote mark.
622 if docstring[0] == quote_char:
623 docstring = " " + docstring
624 if docstring[-1] == quote_char:
625 docstring += " "
626 if docstring[-1] == "\\":
627 backslash_count = len(docstring) - len(docstring.rstrip("\\"))
628 if backslash_count % 2:
629 # Odd number of tailing backslashes, add some padding to
630 # avoid escaping the closing string quote.
631 docstring += " "
632 has_trailing_backslash = True
633 elif not docstring_started_empty:
634 docstring = " "
636 # We could enforce triple quotes at this point.
637 quote = quote_char * quote_len
639 # It's invalid to put closing single-character quotes on a new line.
640 if quote_len == 3:
641 # We need to find the length of the last line of the docstring
642 # to find if we can add the closing quotes to the line without
643 # exceeding the maximum line length.
644 # If docstring is one line, we don't put the closing quotes on a
645 # separate line because it looks ugly (#3320).
646 lines = docstring.splitlines()
647 last_line_length = (
648 len(lines[-1]) if docstring and not docstring.endswith("\n") else 0
649 )
651 # If adding closing quotes would cause the last line to exceed
652 # the maximum line length, and the closing quote is not
653 # prefixed by a newline then put a line break before
654 # the closing quotes
655 if (
656 len(lines) > 1
657 and last_line_length + quote_len > self.mode.line_length
658 and len(indent) + quote_len <= self.mode.line_length
659 and not has_trailing_backslash
660 ):
661 if leaf.value[-1 - quote_len] == "\n":
662 leaf.value = prefix + quote + docstring + quote
663 else:
664 leaf.value = prefix + quote + docstring + "\n" + indent + quote
665 else:
666 leaf.value = prefix + quote + docstring + quote
667 else:
668 leaf.value = prefix + quote + docstring + quote
670 if self.mode.string_normalization and leaf.type == token.STRING:
671 leaf.value = normalize_string_prefix(leaf.value, self.mode)
672 leaf.value = normalize_string_quotes(leaf.value)
673 yield from self.visit_default(leaf)
675 def visit_NUMBER(self, leaf: Leaf) -> Iterator[Line]:
676 normalize_numeric_literal(leaf)
677 yield from self.visit_default(leaf)
679 def visit_atom(self, node: Node) -> Iterator[Line]:
680 """Visit any atom"""
681 if (
682 Preview.remove_redundant_generator_parentheses in self.mode
683 and _has_redundant_generator_parentheses(node)
684 ):
685 maybe_make_parens_invisible_in_atom(
686 node,
687 parent=node.parent or node,
688 mode=self.mode,
689 features=self.features,
690 )
692 if (
693 Preview.remove_redundant_list_parentheses in self.mode
694 and _has_redundant_list_parentheses(node)
695 ):
696 maybe_make_parens_invisible_in_atom(
697 node,
698 parent=node.parent or node,
699 mode=self.mode,
700 features=self.features,
701 )
703 if len(node.children) == 3:
704 first = node.children[0]
705 last = node.children[-1]
706 if (first.type == token.LSQB and last.type == token.RSQB) or (
707 first.type == token.LBRACE and last.type == token.RBRACE
708 ):
709 # Lists or sets of one item
710 maybe_make_parens_invisible_in_atom(
711 node.children[1],
712 parent=node,
713 mode=self.mode,
714 features=self.features,
715 )
717 yield from self.visit_default(node)
719 def visit_fstring(self, node: Node) -> Iterator[Line]:
720 # If the fstring was converted to a STANDALONE_COMMENT by
721 # normalize_fmt_off (e.g. it was inside a # fmt: off block),
722 # skip the fstring-to-string conversion and just visit normally.
723 if any(child.type == STANDALONE_COMMENT for child in node.children):
724 yield from self.visit_default(node)
725 return
726 normalize_fstring_unicode_escape_sequences(node)
727 # currently we don't want to format and split f-strings at all.
728 string_leaf = fstring_tstring_to_string(node)
729 node.replace(string_leaf)
730 if "\\" in string_leaf.value and any(
731 "\\" in str(child)
732 for child in node.children
733 if child.type == syms.fstring_replacement_field
734 ):
735 # string normalization doesn't account for nested quotes,
736 # causing breakages. skip normalization when nested quotes exist
737 yield from self.visit_default(string_leaf)
738 return
739 yield from self.visit_STRING(string_leaf)
741 def visit_tstring(self, node: Node) -> Iterator[Line]:
742 # If the tstring was converted to a STANDALONE_COMMENT by
743 # normalize_fmt_off, skip the conversion and just visit normally.
744 if any(child.type == STANDALONE_COMMENT for child in node.children):
745 yield from self.visit_default(node)
746 return
747 normalize_fstring_unicode_escape_sequences(node)
748 # currently we don't want to format and split t-strings at all.
749 string_leaf = fstring_tstring_to_string(node)
750 node.replace(string_leaf)
751 if "\\" in string_leaf.value and any(
752 "\\" in str(child)
753 for child in node.children
754 if child.type == syms.tstring_replacement_field
755 ):
756 # string normalization doesn't account for nested quotes,
757 # causing breakages. skip normalization when nested quotes exist
758 yield from self.visit_default(string_leaf)
759 return
760 yield from self.visit_STRING(string_leaf)
762 # TODO: Uncomment Implementation to format f-string children
763 # fstring_start = node.children[0]
764 # fstring_end = node.children[-1]
765 # assert isinstance(fstring_start, Leaf)
766 # assert isinstance(fstring_end, Leaf)
768 # quote_char = fstring_end.value[0]
769 # quote_idx = fstring_start.value.index(quote_char)
770 # prefix, quote = (
771 # fstring_start.value[:quote_idx],
772 # fstring_start.value[quote_idx:]
773 # )
775 # if not is_docstring(node, self.mode):
776 # prefix = normalize_string_prefix(prefix, self.mode)
778 # assert quote == fstring_end.value
780 # is_raw_fstring = "r" in prefix or "R" in prefix
781 # middles = [
782 # leaf
783 # for leaf in node.leaves()
784 # if leaf.type == token.FSTRING_MIDDLE
785 # ]
787 # if self.mode.string_normalization:
788 # middles, quote = normalize_fstring_quotes(quote, middles, is_raw_fstring)
790 # fstring_start.value = prefix + quote
791 # fstring_end.value = quote
793 # yield from self.visit_default(node)
795 def visit_comp_for(self, node: Node) -> Iterator[Line]:
796 if (
797 Preview.remove_redundant_unpacking_parentheses in self.mode
798 and len(node.children) > 1
799 ):
800 _normalize_unpacking_targets(
801 node.children[1], mode=self.mode, features=self.features
802 )
803 if Preview.wrap_comprehension_in in self.mode:
804 normalize_invisible_parens(
805 node, parens_after={"in"}, mode=self.mode, features=self.features
806 )
807 yield from self.visit_default(node)
809 def visit_old_comp_for(self, node: Node) -> Iterator[Line]:
810 yield from self.visit_comp_for(node)
812 def __post_init__(self) -> None:
813 """You are in a twisty little maze of passages."""
814 self.current_line = Line(mode=self.mode)
816 v = self.visit_stmt
817 Ø: set[str] = set()
818 self.visit_assert_stmt = partial(v, keywords={"assert"}, parens={"assert", ","})
819 self.visit_if_stmt = partial(
820 v, keywords={"if", "else", "elif"}, parens={"if", "elif"}
821 )
822 self.visit_while_stmt = partial(v, keywords={"while", "else"}, parens={"while"})
823 self.visit_for_stmt = partial(v, keywords={"for", "else"}, parens={"for", "in"})
824 self.visit_try_stmt = partial(
825 v, keywords={"try", "except", "else", "finally"}, parens=Ø
826 )
827 self.visit_except_clause = partial(v, keywords={"except"}, parens={"except"})
828 self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"})
829 self.visit_classdef = partial(v, keywords={"class"}, parens=Ø)
831 self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS)
832 self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"})
833 self.visit_yield_expr = partial(
834 v,
835 keywords=Ø,
836 parens=(
837 {"yield"} if Preview.parenthesize_tuple_in_yield in self.mode else Ø
838 ),
839 )
840 self.visit_import_from = partial(v, keywords=Ø, parens={"import"})
841 self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"})
842 self.visit_async_funcdef = self.visit_async_stmt
843 self.visit_decorated = self.visit_decorators
845 # PEP 634
846 self.visit_match_stmt = self.visit_match_case
847 self.visit_case_block = self.visit_match_case
848 self.visit_guard = partial(v, keywords=Ø, parens={"if"})
851# Remove when `simplify_power_operator_hugging` becomes stable.
852def _hugging_power_ops_line_to_string(
853 line: Line,
854 features: Collection[Feature],
855 mode: Mode,
856) -> str | None:
857 try:
858 return line_to_string(next(hug_power_op(line, features, mode)))
859 except CannotTransform:
860 return None
863def transform_line(
864 line: Line, mode: Mode, features: Collection[Feature] = ()
865) -> Iterator[Line]:
866 """Transform a `line`, potentially splitting it into many lines.
868 They should fit in the allotted `line_length` but might not be able to.
870 `features` are syntactical features that may be used in the output.
871 """
872 if line.is_comment:
873 yield line
874 return
876 line_str = line_to_string(line)
878 if Preview.simplify_power_operator_hugging in mode:
879 line_str_hugging_power_ops = line_str
880 else:
881 # We need the line string when power operators are hugging to determine if we
882 # should split the line. Default to line_str, if no power operator are present
883 # on the line.
884 line_str_hugging_power_ops = (
885 _hugging_power_ops_line_to_string(line, features, mode) or line_str
886 )
888 ll = mode.line_length
889 sn = mode.string_normalization
890 string_merge = StringMerger(ll, sn)
891 string_paren_strip = StringParenStripper(ll, sn)
892 string_split = StringSplitter(ll, sn)
893 string_paren_wrap = StringParenWrapper(ll, sn)
895 transformers: list[Transformer]
896 if (
897 not line.contains_uncollapsable_type_comments()
898 and not line.should_split_rhs
899 and not line.magic_trailing_comma
900 and (
901 is_line_short_enough(line, mode=mode, line_str=line_str_hugging_power_ops)
902 or line.contains_unsplittable_type_ignore()
903 )
904 and not (line.inside_brackets and line.contains_standalone_comments())
905 and not line.contains_implicit_multiline_string_with_comments()
906 ):
907 # Only apply basic string preprocessing, since lines shouldn't be split here.
908 if Preview.string_processing in mode:
909 transformers = [string_merge, string_paren_strip]
910 else:
911 transformers = []
912 elif line.is_def and not should_split_funcdef_with_rhs(line, mode):
913 transformers = [left_hand_split]
914 else:
916 if Preview.string_processing in mode:
917 if line.inside_brackets:
918 transformers = [
919 string_merge,
920 string_paren_strip,
921 string_split,
922 delimiter_split,
923 type_ignore_comment_split,
924 standalone_comment_split,
925 string_paren_wrap,
926 right_hand_split_with_omits,
927 ]
928 if _is_implicit_concatenation_value(line):
929 # Wrap the whole concatenation in parens instead of
930 # splitting it apart first (#3855).
931 transformers.remove(string_paren_wrap)
932 transformers.insert(3, string_paren_wrap)
933 else:
934 transformers = [
935 string_merge,
936 string_paren_strip,
937 string_split,
938 string_paren_wrap,
939 right_hand_split_with_omits,
940 ]
941 else:
942 if line.inside_brackets:
943 transformers = [
944 delimiter_split,
945 type_ignore_comment_split,
946 standalone_comment_split,
947 right_hand_split_with_omits,
948 ]
949 else:
950 transformers = [right_hand_split_with_omits]
952 if Preview.simplify_power_operator_hugging not in mode:
953 # It's always safe to attempt hugging of power operations and pretty much every
954 # line could match.
955 transformers.append(hug_power_op)
957 for transform in transformers:
958 # We are accumulating lines in `result` because we might want to abort
959 # mission and return the original line in the end, or attempt a different
960 # split altogether.
961 try:
962 result = run_transformer(line, transform, mode, features, line_str=line_str)
963 except CannotTransform:
964 continue
965 else:
966 yield from result
967 break
969 else:
970 # A leftover standalone comment would render inline and break the
971 # second pass with a parse error (#4296). Force a split so the
972 # output is at least valid Python.
973 if line.contains_standalone_comments():
974 yield from _force_standalone_comment_split(line)
975 else:
976 yield line
979def _is_implicit_concatenation_value(line: Line) -> bool:
980 """Is `line` something like `a=STRING STRING`, where the only delimiters are
981 between the implicitly concatenated strings?"""
982 if line.leaves[0].type == token.STRING:
983 return False
985 exclude = set()
986 if line.leaves[-1].type == token.COMMA:
987 exclude.add(id(line.leaves[-1]))
988 try:
989 max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude)
990 except ValueError:
991 return False
993 return max_priority == STRING_PRIORITY
996def should_split_funcdef_with_rhs(line: Line, mode: Mode) -> bool:
997 """If a funcdef has a magic trailing comma in the return type, then we should first
998 split the line with rhs to respect the comma.
999 """
1000 return_type_leaves: list[Leaf] = []
1001 in_return_type = False
1003 for leaf in line.leaves:
1004 if leaf.type == token.COLON:
1005 in_return_type = False
1006 if in_return_type:
1007 return_type_leaves.append(leaf)
1008 if leaf.type == token.RARROW:
1009 in_return_type = True
1011 # using `bracket_split_build_line` will mess with whitespace, so we duplicate a
1012 # couple lines from it.
1013 result = Line(mode=line.mode, depth=line.depth)
1014 leaves_to_track = get_leaves_inside_matching_brackets(return_type_leaves)
1015 for leaf in return_type_leaves:
1016 result.append(
1017 leaf,
1018 preformatted=True,
1019 track_bracket=id(leaf) in leaves_to_track,
1020 )
1022 first_visible_return_leaf = next(
1023 (leaf for leaf in return_type_leaves if leaf.value), None
1024 )
1025 return result.magic_trailing_comma is not None or (
1026 first_visible_return_leaf is not None
1027 and first_visible_return_leaf.type == token.STRING
1028 and not is_line_short_enough(result, mode=mode)
1029 )
1032class _BracketSplitComponent(Enum):
1033 head = auto()
1034 body = auto()
1035 tail = auto()
1038def left_hand_split(
1039 line: Line, _features: Collection[Feature], mode: Mode
1040) -> Iterator[Line]:
1041 """Split line into many lines, starting with the first matching bracket pair.
1043 Note: this usually looks weird, only use this for function definitions.
1044 Prefer RHS otherwise. This is why this function is not symmetrical with
1045 :func:`right_hand_split` which also handles optional parentheses.
1046 """
1047 for leaf_type in [token.LPAR, token.LSQB]:
1048 tail_leaves: list[Leaf] = []
1049 body_leaves: list[Leaf] = []
1050 head_leaves: list[Leaf] = []
1051 current_leaves = head_leaves
1052 matching_bracket: Leaf | None = None
1053 depth = 0
1054 for index, leaf in enumerate(line.leaves):
1055 if index == 2 and leaf.type == token.LSQB:
1056 # A [ at index 2 means this is a type param, so start
1057 # tracking the depth
1058 depth += 1
1059 elif depth > 0:
1060 if leaf.type == token.LSQB:
1061 depth += 1
1062 elif leaf.type == token.RSQB:
1063 depth -= 1
1064 if (
1065 current_leaves is body_leaves
1066 and leaf.type in CLOSING_BRACKETS
1067 and leaf.opening_bracket is matching_bracket
1068 and isinstance(matching_bracket, Leaf)
1069 # If the code is still on LPAR and we are inside a type
1070 # param, ignore the match since this is searching
1071 # for the function arguments
1072 and not (leaf_type == token.LPAR and depth > 0)
1073 ):
1074 ensure_visible(leaf)
1075 ensure_visible(matching_bracket)
1076 current_leaves = tail_leaves if body_leaves else head_leaves
1077 current_leaves.append(leaf)
1078 if current_leaves is head_leaves:
1079 if leaf.type == leaf_type and (
1080 not (leaf_type == token.LPAR and depth > 0)
1081 ):
1082 matching_bracket = leaf
1083 current_leaves = body_leaves
1084 if matching_bracket and tail_leaves:
1085 break
1086 if not matching_bracket or not tail_leaves:
1087 raise CannotSplit("No brackets found")
1089 head = bracket_split_build_line(
1090 head_leaves, line, matching_bracket, component=_BracketSplitComponent.head
1091 )
1092 body = bracket_split_build_line(
1093 body_leaves, line, matching_bracket, component=_BracketSplitComponent.body
1094 )
1095 tail = bracket_split_build_line(
1096 tail_leaves, line, matching_bracket, component=_BracketSplitComponent.tail
1097 )
1098 bracket_split_succeeded_or_raise(head, body, tail)
1099 for result in (head, body, tail):
1100 if result:
1101 yield result
1104def right_hand_split(
1105 line: Line,
1106 mode: Mode,
1107 features: Collection[Feature] = (),
1108 omit: Collection[LeafID] = (),
1109) -> Iterator[Line]:
1110 """Split line into many lines, starting with the last matching bracket pair.
1112 If the split was by optional parentheses, attempt splitting without them, too.
1113 `omit` is a collection of closing bracket IDs that shouldn't be considered for
1114 this split.
1116 Note: running this function modifies `bracket_depth` on the leaves of `line`.
1117 """
1118 rhs_result = _first_right_hand_split(line, omit=omit)
1119 yield from _maybe_split_omitting_optional_parens(
1120 rhs_result, line, mode, features=features, omit=omit
1121 )
1124def right_hand_split_with_omits(
1125 line: Line, features: Collection[Feature], mode: Mode
1126) -> Iterator[Line]:
1127 """Wraps calls to `right_hand_split`.
1129 The calls increasingly `omit` right-hand trailers (bracket pairs with
1130 content), meaning the trailers get glued together to split on another
1131 bracket pair instead.
1132 """
1133 fallback_omit: set[LeafID] | None = None
1134 first_lines: list[Line] | None = None
1136 prefix_lengths: dict[LeafID, int] = {}
1137 curr_len = 4 * line.depth
1138 for leaf in line.leaves:
1139 prefix_lengths[id(leaf)] = curr_len
1140 curr_len += len(leaf.prefix) + len(leaf.value)
1142 for omit in generate_trailers_to_omit(line, mode.line_length):
1143 if omit:
1144 target_opening: Leaf | None = None
1145 for leaf in reversed(line.leaves):
1146 if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit:
1147 target_opening = leaf.opening_bracket
1148 break
1149 # If the candidate split point is a concrete bracket whose prefix length
1150 # from the start of the line already exceeds line_length, the head line
1151 # is mathematically guaranteed to exceed line_length as well.
1152 if (
1153 target_opening is not None
1154 and target_opening.value
1155 and prefix_lengths.get(id(target_opening), 0) > mode.line_length
1156 ):
1157 continue
1159 lines = list(right_hand_split(line, mode, features, omit=omit))
1160 if first_lines is None and not omit:
1161 first_lines = lines
1162 # Note: this check is only able to figure out if the first line of the
1163 # *current* transformation fits in the line length. This is true only
1164 # for simple cases. All others require running more transforms via
1165 # `transform_line()`. This check doesn't know if those would succeed.
1166 if is_line_short_enough(lines[0], mode=mode) or (
1167 omit
1168 and (
1169 _over_length_only_due_to_bracket_comment(lines[0], mode)
1170 if Preview.keep_trailers_on_bracket_comment_overflow in mode
1171 else _over_length_only_due_to_subscript_comment(lines[0], mode)
1172 )
1173 ):
1174 if (
1175 Preview.fix_magic_trailing_comma_trailer_split in mode
1176 and line.magic_trailing_comma
1177 and lines[0].magic_trailing_comma
1178 ):
1179 # The head still has to explode on its magic trailing comma, so
1180 # splitting this trailer too is wasted. Prefer a later omit that
1181 # splits on the magic trailing comma's own brackets.
1182 if fallback_omit is None:
1183 fallback_omit = set(omit)
1184 continue
1185 yield from lines
1186 return
1188 if fallback_omit is not None:
1189 yield from right_hand_split(line, mode, features=features, omit=fallback_omit)
1190 return
1192 # All splits failed, best effort split with no omits.
1193 # This mostly happens to multiline strings that are by definition
1194 # reported as not fitting a single line, as well as lines that contain
1195 # trailing commas (those have to be exploded).
1196 if first_lines is not None:
1197 yield from first_lines
1198 else:
1199 yield from right_hand_split(line, mode, features=features)
1202def _first_right_hand_split(
1203 line: Line,
1204 omit: Collection[LeafID] = (),
1205) -> RHSResult:
1206 """Split the line into head, body, tail starting with the last bracket pair.
1208 Note: this function should not have side effects. It's relied upon by
1209 _maybe_split_omitting_optional_parens to get an opinion whether to prefer
1210 splitting on the right side of an assignment statement.
1211 """
1212 tail_leaves: list[Leaf] = []
1213 body_leaves: list[Leaf] = []
1214 head_leaves: list[Leaf] = []
1215 current_leaves = tail_leaves
1216 opening_bracket: Leaf | None = None
1217 closing_bracket: Leaf | None = None
1218 for leaf in reversed(line.leaves):
1219 if current_leaves is body_leaves:
1220 if leaf is opening_bracket:
1221 current_leaves = head_leaves if body_leaves else tail_leaves
1222 current_leaves.append(leaf)
1223 if current_leaves is tail_leaves:
1224 if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit:
1225 opening_bracket = leaf.opening_bracket
1226 closing_bracket = leaf
1227 current_leaves = body_leaves
1228 if not (opening_bracket and closing_bracket and head_leaves):
1229 # If there is no opening or closing_bracket that means the split failed and
1230 # all content is in the tail. Otherwise, if `head_leaves` are empty, it means
1231 # the matching `opening_bracket` wasn't available on `line` anymore.
1232 raise CannotSplit("No brackets found")
1234 tail_leaves.reverse()
1235 body_leaves.reverse()
1236 head_leaves.reverse()
1238 body: Line | None = None
1239 if (
1240 Preview.hug_parens_with_braces_and_square_brackets in line.mode
1241 and tail_leaves[0].value
1242 and tail_leaves[0].opening_bracket is head_leaves[-1]
1243 ):
1244 inner_body_leaves = list(body_leaves)
1245 hugged_opening_leaves: list[Leaf] = []
1246 hugged_closing_leaves: list[Leaf] = []
1247 is_unpacking = body_leaves[0].type in [token.STAR, token.DOUBLESTAR]
1248 unpacking_offset: int = 1 if is_unpacking else 0
1249 while (
1250 len(inner_body_leaves) >= 2 + unpacking_offset
1251 and inner_body_leaves[-1].type in CLOSING_BRACKETS
1252 and inner_body_leaves[-1].opening_bracket
1253 is inner_body_leaves[unpacking_offset]
1254 ):
1255 if unpacking_offset:
1256 hugged_opening_leaves.append(inner_body_leaves.pop(0))
1257 unpacking_offset = 0
1258 hugged_opening_leaves.append(inner_body_leaves.pop(0))
1259 hugged_closing_leaves.insert(0, inner_body_leaves.pop())
1261 if hugged_opening_leaves and inner_body_leaves:
1262 inner_body = bracket_split_build_line(
1263 inner_body_leaves,
1264 line,
1265 hugged_opening_leaves[-1],
1266 component=_BracketSplitComponent.body,
1267 )
1268 if (
1269 line.mode.magic_trailing_comma
1270 and inner_body_leaves[-1].type == token.COMMA
1271 ):
1272 should_hug = True
1273 else:
1274 line_length = line.mode.line_length - sum(
1275 len(str(leaf))
1276 for leaf in hugged_opening_leaves + hugged_closing_leaves
1277 )
1278 if is_line_short_enough(
1279 inner_body, mode=replace(line.mode, line_length=line_length)
1280 ):
1281 # Do not hug if it fits on a single line.
1282 should_hug = False
1283 else:
1284 should_hug = True
1285 if should_hug and (
1286 _hugging_merges_type_ignores(line, head_leaves, hugged_opening_leaves)
1287 or _hugging_merges_type_ignores(
1288 line, hugged_closing_leaves, tail_leaves
1289 )
1290 ):
1291 # Hugging joins these leaves onto one physical line, and their
1292 # trailing comments come along. `type: ignore` is recorded per
1293 # line by the AST, so two of them landing on the same line would
1294 # drop one and make the output non-equivalent.
1295 should_hug = False
1296 if should_hug:
1297 body_leaves = inner_body_leaves
1298 head_leaves.extend(hugged_opening_leaves)
1299 tail_leaves = hugged_closing_leaves + tail_leaves
1300 body = inner_body # No need to re-calculate the body again later.
1302 head = bracket_split_build_line(
1303 head_leaves, line, opening_bracket, component=_BracketSplitComponent.head
1304 )
1305 if body is None:
1306 body = bracket_split_build_line(
1307 body_leaves, line, opening_bracket, component=_BracketSplitComponent.body
1308 )
1309 tail = bracket_split_build_line(
1310 tail_leaves, line, opening_bracket, component=_BracketSplitComponent.tail
1311 )
1312 bracket_split_succeeded_or_raise(head, body, tail)
1313 return RHSResult(head, body, tail, opening_bracket, closing_bracket)
1316def _maybe_split_omitting_optional_parens(
1317 rhs: RHSResult,
1318 line: Line,
1319 mode: Mode,
1320 features: Collection[Feature] = (),
1321 omit: Collection[LeafID] = (),
1322) -> Iterator[Line]:
1323 split_symmetric_collection_binops = (
1324 Preview.symmetric_collection_operations in mode
1325 and rhs.opening_bracket.type == token.LPAR
1326 and not rhs.opening_bracket.value
1327 and rhs.closing_bracket.type == token.RPAR
1328 and not rhs.closing_bracket.value
1329 and is_symmetric_collection_binop(rhs.body, mode.line_length)
1330 )
1331 if (
1332 Feature.FORCE_OPTIONAL_PARENTHESES not in features
1333 # the opening bracket is an optional paren
1334 and rhs.opening_bracket.type == token.LPAR
1335 and not rhs.opening_bracket.value
1336 # the closing bracket is an optional paren
1337 and rhs.closing_bracket.type == token.RPAR
1338 and not rhs.closing_bracket.value
1339 # it's not an import (optional parens are the only thing we can split on
1340 # in this case; attempting a split without them is a waste of time)
1341 and not line.is_import
1342 # and we can actually remove the parens
1343 and can_omit_invisible_parens(rhs, mode.line_length, mode)
1344 ):
1345 omit = {id(rhs.closing_bracket), *omit}
1346 try:
1347 # The RHSResult Omitting Optional Parens.
1348 rhs_oop = _first_right_hand_split(line, omit=omit)
1349 if _prefer_split_rhs_oop_over_rhs(rhs_oop, rhs, mode):
1350 yield from _maybe_split_omitting_optional_parens(
1351 rhs_oop, line, mode, features=features, omit=omit
1352 )
1353 return
1354 elif Preview.avoid_parens_for_unbreakable_rhs_in_assignments in mode:
1355 raise CannotSplit(
1356 "Alternative split not preferred, falling back to original RHS"
1357 )
1359 except CannotSplit as e:
1360 # For chained assignments we want to use the previous successful split
1361 if line.is_chained_assignment:
1362 pass
1364 elif (
1365 not can_be_split(rhs.body)
1366 and not is_line_short_enough(rhs.body, mode=mode)
1367 and not (
1368 Preview.wrap_long_dict_values_in_parens in mode
1369 and rhs.opening_bracket.parent
1370 and rhs.opening_bracket.parent.parent
1371 and rhs.opening_bracket.parent.parent.type == syms.dictsetmaker
1372 )
1373 and not (
1374 rhs.opening_bracket.parent
1375 and rhs.opening_bracket.parent.parent
1376 and rhs.opening_bracket.parent.parent.type == syms.case_block
1377 )
1378 ):
1379 raise CannotSplit(
1380 "Splitting failed, body is still too long and can't be split."
1381 ) from e
1383 elif (
1384 rhs.head.contains_multiline_strings()
1385 or rhs.tail.contains_multiline_strings()
1386 ):
1387 raise CannotSplit(
1388 "The current optional pair of parentheses is bound to fail to"
1389 " satisfy the splitting algorithm because the head or the tail"
1390 " contains multiline strings which by definition never fit one"
1391 " line."
1392 ) from e
1394 ensure_visible(rhs.opening_bracket)
1395 ensure_visible(rhs.closing_bracket)
1396 if split_symmetric_collection_binops:
1397 rhs.body.should_split_rhs = True
1398 for result in (rhs.head, rhs.body, rhs.tail):
1399 if result:
1400 yield result
1403def _prefer_split_rhs_oop_over_rhs(
1404 rhs_oop: RHSResult, rhs: RHSResult, mode: Mode
1405) -> bool:
1406 """
1407 Returns whether we should prefer the result from a split omitting optional parens
1408 (rhs_oop) over the original (rhs).
1409 """
1410 # contains unsplittable type ignore
1411 if (
1412 rhs_oop.head.contains_unsplittable_type_ignore()
1413 or rhs_oop.body.contains_unsplittable_type_ignore()
1414 or rhs_oop.tail.contains_unsplittable_type_ignore()
1415 ):
1416 return True
1418 # Retain optional parens around dictionary values
1419 if (
1420 Preview.wrap_long_dict_values_in_parens in mode
1421 and rhs.opening_bracket.parent
1422 and rhs.opening_bracket.parent.parent
1423 and rhs.opening_bracket.parent.parent.type == syms.dictsetmaker
1424 and rhs.body.bracket_tracker.delimiters
1425 ):
1426 # Unless the split is inside the key
1427 return any(leaf.type == token.COLON for leaf in rhs_oop.tail.leaves)
1429 # the split is right after `=`
1430 if not (len(rhs.head.leaves) >= 2 and rhs.head.leaves[-2].type == token.EQUAL):
1431 return True
1433 # the left side of assignment contains brackets
1434 if not any(leaf.type in BRACKETS for leaf in rhs.head.leaves[:-1]):
1435 return True
1437 # the left side of assignment is short enough (the -1 is for the ending optional
1438 # paren)
1439 if not is_line_short_enough(
1440 rhs.head, mode=replace(mode, line_length=mode.line_length - 1)
1441 ):
1442 return True
1444 # the left side of assignment won't explode further because of magic trailing comma
1445 if rhs.head.magic_trailing_comma is not None:
1446 return True
1448 # If we have multiple targets, we prefer more `=`s on the head vs pushing them to
1449 # the body
1450 rhs_head_equal_count = [leaf.type for leaf in rhs.head.leaves].count(token.EQUAL)
1451 rhs_oop_head_equal_count = [leaf.type for leaf in rhs_oop.head.leaves].count(
1452 token.EQUAL
1453 )
1454 if rhs_head_equal_count > 1 and rhs_head_equal_count > rhs_oop_head_equal_count:
1455 return False
1457 has_closing_bracket_after_assign = False
1458 for leaf in reversed(rhs_oop.head.leaves):
1459 if leaf.type == token.EQUAL:
1460 break
1461 if leaf.type in CLOSING_BRACKETS:
1462 has_closing_bracket_after_assign = True
1463 break
1464 return (
1465 # contains matching brackets after the `=` (done by checking there is a
1466 # closing bracket)
1467 has_closing_bracket_after_assign
1468 or (
1469 # the split is actually from inside the optional parens (done by checking
1470 # the first line still contains the `=`)
1471 any(leaf.type == token.EQUAL for leaf in rhs_oop.head.leaves)
1472 # the first line is short enough
1473 and is_line_short_enough(rhs_oop.head, mode=mode)
1474 )
1475 )
1478def bracket_split_succeeded_or_raise(head: Line, body: Line, tail: Line) -> None:
1479 """Raise :exc:`CannotSplit` if the last left- or right-hand split failed.
1481 Do nothing otherwise.
1483 A left- or right-hand split is based on a pair of brackets. Content before
1484 (and including) the opening bracket is left on one line, content inside the
1485 brackets is put on a separate line, and finally content starting with and
1486 following the closing bracket is put on a separate line.
1488 Those are called `head`, `body`, and `tail`, respectively. If the split
1489 produced the same line (all content in `head`) or ended up with an empty `body`
1490 and the `tail` is just the closing bracket, then it's considered failed.
1491 """
1492 tail_len = len(str(tail).strip())
1493 if not body:
1494 if tail_len == 0:
1495 raise CannotSplit("Splitting brackets produced the same line")
1497 elif tail_len < 3:
1498 raise CannotSplit(
1499 f"Splitting brackets on an empty body to save {tail_len} characters is"
1500 " not worth it"
1501 )
1504def _ensure_trailing_comma(
1505 leaves: list[Leaf], original: Line, opening_bracket: Leaf
1506) -> bool:
1507 if not leaves:
1508 return False
1509 # Ensure a trailing comma for imports
1510 if original.is_import:
1511 return True
1512 # ...and standalone function arguments
1513 if not original.is_def:
1514 return False
1515 if opening_bracket.value != "(":
1516 return False
1517 # Don't add commas if we already have any commas
1518 if any(
1519 leaf.type == token.COMMA and not is_part_of_annotation(leaf) for leaf in leaves
1520 ):
1521 return False
1523 # Find a leaf with a parent (comments don't have parents)
1524 leaf_with_parent = next((leaf for leaf in leaves if leaf.parent), None)
1525 if leaf_with_parent is None:
1526 return True
1527 # Don't add commas inside parenthesized return annotations
1528 if get_annotation_type(leaf_with_parent) == "return":
1529 return False
1530 # Don't add commas inside PEP 604 unions
1531 if (
1532 leaf_with_parent.parent
1533 and leaf_with_parent.parent.next_sibling
1534 and leaf_with_parent.parent.next_sibling.type == token.VBAR
1535 ):
1536 return False
1537 return True
1540def _hugging_merges_type_ignores(line: Line, *leaf_groups: Iterable[Leaf]) -> bool:
1541 """Return True if hugging these groups would put two `type: ignore`s on a line."""
1542 seen = 0
1543 for leaves in leaf_groups:
1544 for leaf in leaves:
1545 for comment in line.comments_after(leaf):
1546 if is_type_ignore_comment(comment, mode=line.mode):
1547 seen += 1
1548 if seen > 1:
1549 return True
1550 return False
1553def bracket_split_build_line(
1554 leaves: list[Leaf],
1555 original: Line,
1556 opening_bracket: Leaf,
1557 *,
1558 component: _BracketSplitComponent,
1559) -> Line:
1560 """Return a new line with given `leaves` and respective comments from `original`.
1562 If it's the head component, brackets will be tracked so trailing commas are
1563 respected.
1565 If it's the body component, the result line is one-indented inside brackets and as
1566 such has its first leaf's prefix normalized and a trailing comma added when
1567 expected.
1568 """
1569 result = Line(mode=original.mode, depth=original.depth)
1570 if component is _BracketSplitComponent.body:
1571 result.inside_brackets = True
1572 result.depth += 1
1573 if _ensure_trailing_comma(leaves, original, opening_bracket):
1574 for i in range(len(leaves) - 1, -1, -1):
1575 if leaves[i].type == STANDALONE_COMMENT:
1576 continue
1578 if leaves[i].type != token.COMMA:
1579 new_comma = Leaf(token.COMMA, ",")
1580 leaves.insert(i + 1, new_comma)
1581 break
1583 leaves_to_track: set[LeafID] = set()
1584 if component is _BracketSplitComponent.head:
1585 leaves_to_track = get_leaves_inside_matching_brackets(leaves)
1586 # Populate the line
1587 for leaf in leaves:
1588 result.append(
1589 leaf,
1590 preformatted=True,
1591 track_bracket=id(leaf) in leaves_to_track,
1592 )
1593 for comment_after in original.comments_after(leaf):
1594 result.append(comment_after, preformatted=True)
1595 if component is _BracketSplitComponent.body and should_split_line(
1596 result, opening_bracket
1597 ):
1598 result.should_split_rhs = True
1599 return result
1602def dont_increase_indentation(split_func: Transformer) -> Transformer:
1603 """Normalize prefix of the first leaf in every line returned by `split_func`.
1605 This is a decorator over relevant split functions.
1606 """
1608 @wraps(split_func)
1609 def split_wrapper(
1610 line: Line, features: Collection[Feature], mode: Mode
1611 ) -> Iterator[Line]:
1612 for split_line in split_func(line, features, mode):
1613 split_line.leaves[0].prefix = ""
1614 yield split_line
1616 return split_wrapper
1619def _get_last_non_comment_leaf(line: Line) -> int | None:
1620 for leaf_idx in range(len(line.leaves) - 1, 0, -1):
1621 if line.leaves[leaf_idx].type != STANDALONE_COMMENT:
1622 return leaf_idx
1623 return None
1626def _can_add_trailing_comma(leaf: Leaf, features: Collection[Feature]) -> bool:
1627 if is_vararg(leaf, within={syms.typedargslist}):
1628 return Feature.TRAILING_COMMA_IN_DEF in features
1629 if is_vararg(leaf, within={syms.arglist, syms.argument}):
1630 return Feature.TRAILING_COMMA_IN_CALL in features
1631 return True
1634def _has_unmatched_closing_bracket(line: Line) -> bool:
1635 depth = 0
1636 for leaf in line.leaves:
1637 if leaf.type in OPENING_BRACKETS:
1638 depth += 1
1639 elif leaf.type in CLOSING_BRACKETS:
1640 if depth == 0:
1641 return True
1642 depth -= 1
1643 return False
1646def _safe_add_trailing_comma(safe: bool, delimiter_priority: int, line: Line) -> Line:
1647 if (
1648 safe
1649 and delimiter_priority == COMMA_PRIORITY
1650 and line.leaves[-1].type != token.COMMA
1651 and line.leaves[-1].type != STANDALONE_COMMENT
1652 ):
1653 new_comma = Leaf(token.COMMA, ",")
1654 line.append(new_comma)
1655 return line
1658MIGRATE_COMMENT_DELIMITERS = {STRING_PRIORITY, COMMA_PRIORITY}
1661def _can_defer_lone_comparator_to_rhs(line: Line, rhs: RHSResult, mode: Mode) -> bool:
1662 """Return True if the lone comparator on `line` can defer to right_hand_split.
1664 Caller has already established exactly one delimiter at
1665 `COMPARATOR_PRIORITY`. We defer only when:
1667 - the LHS up to the comparator has no opening brackets, so the existing
1668 "break before the comparator" wouldn't produce a balanced two-sided
1669 split anyway, and
1670 - `right_hand_split` would produce a head that fits in the line length,
1671 so we don't strand `if t` on its own line just to push it back onto an
1672 overflowing single line when the RHS bracket can't be exploded
1673 usefully (e.g. an empty `decode()` paren).
1674 """
1675 past_comparator = False
1676 for leaf in line.leaves:
1677 if leaf.type in OPENING_BRACKETS and not past_comparator:
1678 return False
1679 if not past_comparator and (
1680 line.bracket_tracker.delimiters.get(id(leaf)) == COMPARATOR_PRIORITY
1681 ):
1682 past_comparator = True
1683 return is_line_short_enough(rhs.head, mode=mode)
1686def _can_defer_dict_key_delimiter_to_rhs(
1687 line: Line, rhs: RHSResult, mode: Mode
1688) -> bool:
1689 """Return True if delimiters on a dictionary key can defer to right_hand_split.
1691 When a dictionary key contains operators (like +, -, %, etc.), delimiter_split
1692 would split inside the key instead of allowing the dictionary value to be
1693 wrapped onto a new line. We defer to right_hand_split when the key itself
1694 (along with the opening paren around the value) fits within the line length.
1695 """
1696 colon_idx: int | None = None
1697 for idx, leaf in enumerate(line.leaves):
1698 if (
1699 leaf.type == token.COLON
1700 and leaf.bracket_depth == 0
1701 and leaf.parent
1702 and leaf.parent.type == syms.dictsetmaker
1703 ):
1704 colon_idx = idx
1705 break
1706 if colon_idx is None:
1707 return False
1709 if any(leaf.type == token.RBRACE for leaf in line.leaves[:colon_idx]):
1710 return False
1712 bt = line.bracket_tracker
1713 last_leaf = line.leaves[-1]
1714 try:
1715 delimiter_priority = bt.max_delimiter_priority(exclude={id(last_leaf)})
1716 except ValueError:
1717 return False
1719 if delimiter_priority >= LOGIC_PRIORITY:
1720 return False
1722 for leaf_id, prio in bt.delimiters.items():
1723 if prio == delimiter_priority:
1724 leaf_idx = next(i for i, l in enumerate(line.leaves) if id(l) == leaf_id)
1725 if leaf_idx >= colon_idx:
1726 return False
1728 return is_line_short_enough(rhs.head, mode=mode)
1731@dont_increase_indentation
1732def delimiter_split(
1733 line: Line, features: Collection[Feature], mode: Mode
1734) -> Iterator[Line]:
1735 """Split according to delimiters of the highest priority.
1737 If the appropriate Features are given, the split will add trailing commas
1738 also in function signatures and calls that contain `*` and `**`.
1739 """
1740 if len(line.leaves) == 0:
1741 raise CannotSplit("Line empty") from None
1742 last_leaf = line.leaves[-1]
1744 bt = line.bracket_tracker
1745 try:
1746 delimiter_priority = bt.max_delimiter_priority(exclude={id(last_leaf)})
1747 except ValueError:
1748 raise CannotSplit("No delimiters found") from None
1750 if (
1751 delimiter_priority == DOT_PRIORITY
1752 and bt.delimiter_count_with_priority(delimiter_priority) == 1
1753 ):
1754 raise CannotSplit("Splitting a single attribute from its owner looks wrong")
1756 rhs: RHSResult | None = None
1757 if (
1758 Preview.keep_commented_expressions_together in mode
1759 and line.contains_standalone_comments()
1760 and delimiter_priority != COMMA_PRIORITY
1761 ):
1762 first_comment_idx = next(
1763 i for i, leaf in enumerate(line.leaves) if leaf.type == STANDALONE_COMMENT
1764 )
1765 first_delimiter_idx = next(
1766 i
1767 for i, leaf in enumerate(line.leaves)
1768 if bt.delimiters.get(id(leaf)) == delimiter_priority
1769 )
1770 if first_comment_idx < first_delimiter_idx:
1771 raise CannotSplit("Standalone comments should be split first")
1773 if (
1774 Preview.hug_comparator in mode
1775 and delimiter_priority == COMPARATOR_PRIORITY
1776 and bt.delimiter_count_with_priority(delimiter_priority) == 1
1777 ) or Preview.keep_dict_keys_with_operators in mode:
1778 try:
1779 rhs = _first_right_hand_split(line)
1780 except CannotSplit:
1781 pass
1783 if (
1784 rhs is not None
1785 and Preview.hug_comparator in mode
1786 and delimiter_priority == COMPARATOR_PRIORITY
1787 and bt.delimiter_count_with_priority(delimiter_priority) == 1
1788 and _can_defer_lone_comparator_to_rhs(line, rhs, mode)
1789 ):
1790 raise CannotSplit("Bracketed RHS will explode via right_hand_split")
1792 if (
1793 rhs is not None
1794 and Preview.keep_dict_keys_with_operators in mode
1795 and _can_defer_dict_key_delimiter_to_rhs(line, rhs, mode)
1796 ):
1797 raise CannotSplit("Dict key delimiter will explode via right_hand_split")
1799 current_line = Line(
1800 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1801 )
1802 lowest_depth = sys.maxsize
1803 # If the opening bracket was split onto an earlier line, this line isn't a whole
1804 # collection and a trailing comma at its end would land outside of it.
1805 trailing_comma_safe = not _has_unmatched_closing_bracket(line)
1807 def append_to_line(leaf: Leaf) -> Iterator[Line]:
1808 """Append `leaf` to current line or to new line if appending impossible."""
1809 nonlocal current_line
1810 try:
1811 current_line.append_safe(leaf, preformatted=True)
1812 except ValueError:
1813 yield current_line
1815 current_line = Line(
1816 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1817 )
1818 current_line.append(leaf)
1820 def append_comments(leaf: Leaf) -> Iterator[Line]:
1821 for comment_after in line.comments_after(leaf):
1822 yield from append_to_line(comment_after)
1824 last_non_comment_leaf = _get_last_non_comment_leaf(line)
1825 for leaf_idx, leaf in enumerate(line.leaves):
1826 yield from append_to_line(leaf)
1828 previous_priority = leaf_idx > 0 and bt.delimiters.get(
1829 id(line.leaves[leaf_idx - 1])
1830 )
1831 if (
1832 previous_priority != delimiter_priority
1833 or delimiter_priority in MIGRATE_COMMENT_DELIMITERS
1834 ):
1835 yield from append_comments(leaf)
1837 lowest_depth = min(lowest_depth, leaf.bracket_depth)
1838 if trailing_comma_safe and leaf.bracket_depth == lowest_depth:
1839 trailing_comma_safe = _can_add_trailing_comma(leaf, features)
1841 if last_leaf.type == STANDALONE_COMMENT and leaf_idx == last_non_comment_leaf:
1842 current_line = _safe_add_trailing_comma(
1843 trailing_comma_safe, delimiter_priority, current_line
1844 )
1846 leaf_priority = bt.delimiters.get(id(leaf))
1847 if leaf_priority == delimiter_priority:
1848 if (
1849 leaf_idx + 1 < len(line.leaves)
1850 and delimiter_priority not in MIGRATE_COMMENT_DELIMITERS
1851 ):
1852 yield from append_comments(line.leaves[leaf_idx + 1])
1854 yield current_line
1855 current_line = Line(
1856 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1857 )
1859 if current_line:
1860 current_line = _safe_add_trailing_comma(
1861 trailing_comma_safe, delimiter_priority, current_line
1862 )
1863 yield current_line
1866@dont_increase_indentation
1867def standalone_comment_split(
1868 line: Line, features: Collection[Feature], mode: Mode
1869) -> Iterator[Line]:
1870 """Split standalone comments from the rest of the line."""
1871 if not line.contains_standalone_comments():
1872 raise CannotSplit("Line does not have any standalone comments")
1874 current_line = Line(
1875 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1876 )
1878 def append_to_line(leaf: Leaf) -> Iterator[Line]:
1879 """Append `leaf` to current line or to new line if appending impossible."""
1880 nonlocal current_line
1881 try:
1882 current_line.append_safe(leaf, preformatted=True)
1883 except ValueError:
1884 yield current_line
1886 current_line = Line(
1887 line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1888 )
1889 current_line.append(leaf)
1891 for leaf in line.leaves:
1892 yield from append_to_line(leaf)
1894 for comment_after in line.comments_after(leaf):
1895 yield from append_to_line(comment_after)
1897 if current_line:
1898 yield current_line
1901@dont_increase_indentation
1902def type_ignore_comment_split(
1903 line: Line, features: Collection[Feature], mode: Mode
1904) -> Iterator[Line]:
1905 """Keep multiple type ignores on their original physical lines.
1907 Any other comment in the chain also ends its line: a type ignore that
1908 follows another comment on the same line is no longer a type ignore.
1909 """
1910 if not line.contains_multiple_type_ignores_at_current_depth() or not any(
1911 leaf.type == token.DOT for leaf in line.leaves
1912 ):
1913 raise CannotSplit("Line does not have multiple type ignore comments")
1915 current_line = Line(
1916 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1917 )
1918 for leaf in line.leaves:
1919 current_line.append(leaf, preformatted=True)
1920 comments = line.comments_after(leaf)
1921 for comment in comments:
1922 current_line.append(comment, preformatted=True)
1923 if comments:
1924 yield current_line
1925 current_line = Line(
1926 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1927 )
1929 if current_line:
1930 yield current_line
1933def _force_standalone_comment_split(line: Line) -> Iterator[Line]:
1934 """Last-resort split at every standalone-comment boundary."""
1935 current_line = Line(
1936 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1937 )
1938 for leaf in line.leaves:
1939 if (
1940 current_line.leaves
1941 and (leaf.type == STANDALONE_COMMENT or current_line.is_comment)
1942 # Do not isolate the header colon onto its own line when a
1943 # standalone comment (e.g. from `# fmt: skip`) ends with `)`.
1944 and not (current_line.is_comment and leaf.type == token.COLON)
1945 # Code hidden by `# fmt: skip` / `# fmt: off` that ends in code rather
1946 # than a comment can be followed by the rest of the line.
1947 and not (
1948 leaf.type != STANDALONE_COMMENT
1949 and _is_fmt_pass_converted_ending_in_code(current_line)
1950 )
1951 and not _continues_fmt_skip_line(current_line, leaf)
1952 ):
1953 yield current_line
1954 current_line = Line(
1955 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1956 )
1957 current_line.append(leaf, preformatted=True)
1958 for comment_after in line.comments_after(leaf):
1959 current_line.append(comment_after, preformatted=True)
1960 if current_line:
1961 yield current_line
1964def _is_fmt_pass_converted_ending_in_code(line: Line) -> bool:
1965 """Is `line` a single STANDALONE_COMMENT holding code hidden by `# fmt: skip`
1966 or `# fmt: off` whose last line has no comment?
1968 Any `#` on the last line counts as a comment, even inside a string, so in
1969 doubt the caller keeps splitting.
1970 """
1971 if not line.is_fmt_pass_converted():
1972 return False
1973 return "#" not in line.leaves[0].value.rpartition("\n")[2]
1976def _continues_fmt_skip_line(line: Line, leaf: Leaf) -> bool:
1977 """Is `leaf` code hidden by `# fmt: skip` that starts on the line where the
1978 hidden code at the end of `line` ends?
1980 That happens with two `# fmt: skip` comments in one statement, as in
1981 `x = a if ( # fmt: skip` ... `) else ( # fmt: skip` ... `)`.
1982 """
1983 if not line.leaves:
1984 return False
1985 last = line.leaves[-1]
1986 return (
1987 _is_hidden_by_fmt_skip(last)
1988 and "#" not in last.value.rpartition("\n")[2]
1989 and _is_hidden_by_fmt_skip(leaf)
1990 and not leaf.value.startswith("#")
1991 and "\n" not in leaf.prefix
1992 )
1995def _is_hidden_by_fmt_skip(leaf: Leaf) -> bool:
1996 """Is `leaf` a STANDALONE_COMMENT holding code hidden by `# fmt: skip` or
1997 `# fmt: off`, rather than code left alone by `--line-ranges`?"""
1998 return (
1999 leaf.type == STANDALONE_COMMENT
2000 and leaf.fmt_pass_converted_first_leaf is not None
2001 and leaf.line_ranges_selected is None
2002 )
2005def _is_parenthesized_lambda_or_ternary(node: LN) -> bool:
2006 """Whether `node` is an atom wrapping a lambda or conditional expression,
2007 looking through any redundant nested parentheses.
2009 As a comprehension's iterable, such an expression must keep at least one pair
2010 of parentheses: without them the trailing `for`/`if` clauses would be parsed
2011 as part of the lambda body (or break the ternary), producing invalid code.
2012 """
2013 while (
2014 node.type == syms.atom
2015 and len(node.children) == 3
2016 and is_lpar_token(node.children[0])
2017 and is_rpar_token(node.children[-1])
2018 ):
2019 middle = node.children[1]
2020 if middle.type in {syms.test, syms.lambdef}:
2021 return True
2022 node = middle
2023 return False
2026def _has_redundant_generator_parentheses(node: LN) -> bool:
2027 """Whether `node` adds parentheses around a parenthesized generator."""
2028 if (
2029 node.type != syms.atom
2030 or len(node.children) != 3
2031 or not is_lpar_token(node.children[0])
2032 or not is_rpar_token(node.children[-1])
2033 ):
2034 return False
2036 middle = node.children[1]
2037 return is_generator(middle) or _has_redundant_generator_parentheses(middle)
2040def _is_implicitly_concatenated_string(node: LN) -> bool:
2041 """Return True if node wraps implicitly concatenated strings."""
2042 if not isinstance(node, Node) or node.type != syms.atom or len(node.children) < 3:
2043 return False
2044 if not (is_lpar_token(node.children[0]) and is_rpar_token(node.children[-1])):
2045 return False
2046 middle = node.children[1]
2047 if not (isinstance(middle, Node) and middle.type == syms.atom and middle.children):
2048 return False
2049 first_child = middle.children[0]
2050 return (
2051 first_child.type == token.STRING
2052 or getattr(first_child, "type", None) == syms.fstring
2053 or getattr(first_child, "type", None) == getattr(syms, "tstring", 999)
2054 )
2057def _has_redundant_list_parentheses(node: LN) -> bool:
2058 """Whether `node` adds parentheses around a list."""
2059 if (
2060 node.type != syms.atom
2061 or len(node.children) != 3
2062 or not is_lpar_token(node.children[0])
2063 or not is_rpar_token(node.children[-1])
2064 ):
2065 return False
2067 middle = node.children[1]
2068 return is_list(middle) or _has_redundant_list_parentheses(middle)
2071def _normalize_unpacking_targets(
2072 node: LN,
2073 mode: Mode,
2074 features: Collection[Feature],
2075) -> None:
2076 """Remove redundant parentheses from individual elements in unpacking targets."""
2077 if not isinstance(node, Node):
2078 return
2080 if node.type in (
2081 syms.exprlist,
2082 syms.testlist_star_expr,
2083 syms.testlist_gexp,
2084 syms.testlist,
2085 syms.listmaker,
2086 syms.star_expr,
2087 ):
2088 for child in node.children:
2089 if child.type == syms.atom and not _atom_has_inner_comments(child):
2090 maybe_make_parens_invisible_in_atom(
2091 child,
2092 parent=node,
2093 mode=mode,
2094 features=features,
2095 )
2096 _normalize_unpacking_targets(child, mode=mode, features=features)
2097 elif node.type == syms.atom:
2098 for child in node.children:
2099 if isinstance(child, Node):
2100 _normalize_unpacking_targets(child, mode=mode, features=features)
2103_DISALLOWED_COMPARISON_INNER_TYPES: Final = {
2104 syms.comparison,
2105 syms.not_test,
2106 syms.and_test,
2107 syms.or_test,
2108 syms.test,
2109 syms.lambdef,
2110 syms.namedexpr_test,
2111 syms.testlist_gexp,
2112 syms.yield_expr,
2113 syms.old_lambdef,
2114 syms.old_test,
2115}
2117_BINARY_OP_TYPES: Final = {
2118 syms.arith_expr,
2119 syms.term,
2120 syms.expr,
2121 syms.xor_expr,
2122 syms.and_expr,
2123 syms.shift_expr,
2124}
2127def _is_binary_power(node: LN) -> bool:
2128 """Whether node is an exponentiation expression (e.g. `a ** b`)."""
2129 return (
2130 isinstance(node, Node)
2131 and node.type == syms.power
2132 and any(child.type == token.DOUBLESTAR for child in node.children)
2133 )
2136def _is_parenthesized_atom(node: LN) -> bool:
2137 """Whether node is an atom enclosed in parentheses."""
2138 return (
2139 node.type == syms.atom
2140 and len(node.children) == 3
2141 and is_lpar_token(node.children[0])
2142 and is_rpar_token(node.children[-1])
2143 )
2146def _is_disallowed_comparison_operand(node: LN) -> bool:
2147 """Whether atom `node` encloses an expression that must retain parens in
2148 comparison.
2149 """
2150 if not _is_parenthesized_atom(node):
2151 return False
2153 middle = node.children[1]
2154 return (
2155 middle.type in _DISALLOWED_COMPARISON_INNER_TYPES
2156 or middle.type in _BINARY_OP_TYPES
2157 or _is_binary_power(middle)
2158 )
2161def _normalize_comparison_operands(
2162 node: Node, mode: Mode, features: Collection[Feature]
2163) -> None:
2164 """Remove redundant parentheses around operands in a comparison expression."""
2165 for i in range(0, len(node.children), 2):
2166 operand = node.children[i]
2167 if operand.type == syms.atom and not _is_atom_multiline(operand):
2168 maybe_make_parens_invisible_in_atom(
2169 operand, parent=node, mode=mode, features=features
2170 )
2171 if is_atom_with_invisible_parens(operand) and not any(
2172 "#" in leaf.prefix for leaf in operand.leaves()
2173 ):
2174 # Drop the now invisible parens so the operand splits the same way
2175 # as one written without parentheses. Otherwise the next pass, which
2176 # no longer sees them, can split it differently: parens kept for
2177 # being multiline are joined onto one line and only removed then.
2178 middle = operand.children[1]
2179 middle.prefix = operand.prefix
2180 operand.replace(middle)
2183def normalize_invisible_parens(
2184 node: Node, parens_after: set[str], *, mode: Mode, features: Collection[Feature]
2185) -> None:
2186 """Make existing optional parentheses invisible or create new ones.
2188 `parens_after` is a set of string leaf values immediately after which parens
2189 should be put.
2191 Standardizes on visible parentheses for single-element tuples, and keeps
2192 existing visible parentheses for other tuples and generator expressions.
2193 """
2194 for pc in list_comments(node.prefix, is_endmarker=False, mode=mode):
2195 if contains_fmt_directive(pc.value, FMT_OFF):
2196 # This `node` has a prefix with `# fmt: off`, don't mess with parens.
2197 return
2199 if Preview.remove_redundant_unpacking_parentheses in mode:
2200 if node.type in (syms.for_stmt, syms.comp_for, syms.old_comp_for):
2201 if len(node.children) > 1:
2202 _normalize_unpacking_targets(
2203 node.children[1], mode=mode, features=features
2204 )
2205 elif node.type == syms.expr_stmt:
2206 equal_indices = [
2207 i for i, child in enumerate(node.children) if child.type == token.EQUAL
2208 ]
2209 if equal_indices:
2210 for child in node.children[: equal_indices[-1]]:
2211 if child.type != token.EQUAL:
2212 _normalize_unpacking_targets(
2213 child, mode=mode, features=features
2214 )
2215 elif node.type == syms.del_stmt:
2216 if len(node.children) > 1:
2217 _normalize_unpacking_targets(
2218 node.children[1], mode=mode, features=features
2219 )
2220 elif node.type == syms.with_stmt:
2221 for child in node.children:
2222 if child.type == syms.asexpr_test and len(child.children) > 2:
2223 _normalize_unpacking_targets(
2224 child.children[2], mode=mode, features=features
2225 )
2227 # The multiple context managers grammar has a different pattern, thus this is
2228 # separate from the for-loop below. This possibly wraps them in invisible parens,
2229 # and later will be removed in remove_with_parens when needed.
2230 if node.type == syms.with_stmt:
2231 _maybe_wrap_cms_in_parens(node, mode, features)
2233 check_lpar = False
2234 for index, child in enumerate(list(node.children)):
2235 # Fixes a bug where invisible parens are not properly stripped from
2236 # assignment statements that contain type annotations.
2237 if isinstance(child, Node) and child.type == syms.annassign:
2238 normalize_invisible_parens(
2239 child, parens_after=parens_after, mode=mode, features=features
2240 )
2242 # Fixes a bug where invisible parens are not properly wrapped around
2243 # case blocks.
2244 if isinstance(child, Node) and child.type == syms.case_block:
2245 normalize_invisible_parens(
2246 child, parens_after={"case"}, mode=mode, features=features
2247 )
2249 # Add parentheses around if guards in case blocks
2250 if isinstance(child, Node) and child.type == syms.guard:
2251 normalize_invisible_parens(
2252 child, parens_after={"if"}, mode=mode, features=features
2253 )
2255 # Add parentheses around long tuple unpacking in assignments.
2256 if (
2257 index == 0
2258 and isinstance(child, Node)
2259 and child.type == syms.testlist_star_expr
2260 ):
2261 check_lpar = True
2263 if (
2264 index == 0
2265 and isinstance(child, Node)
2266 and child.type == syms.atom
2267 and node.type == syms.expr_stmt
2268 and not _atom_has_magic_trailing_comma(child, mode)
2269 and not _is_atom_multiline(child)
2270 ):
2271 if _is_parenthesized_annotation_target(node, child):
2272 # One pair is what makes the target non-simple, so any nesting
2273 # inside it is redundant and goes.
2274 inner = child.children[1]
2275 if isinstance(inner, Node) and inner.type == syms.atom:
2276 maybe_make_parens_invisible_in_atom(
2277 inner,
2278 parent=child,
2279 mode=mode,
2280 features=features,
2281 )
2282 elif maybe_make_parens_invisible_in_atom(
2283 child,
2284 parent=node,
2285 mode=mode,
2286 features=features,
2287 remove_brackets_around_comma=True,
2288 allow_star_expr=True,
2289 ):
2290 wrap_in_parentheses(node, child, visible=False, index=index)
2291 elif (
2292 is_atom_with_invisible_parens(child)
2293 and not is_tuple(child)
2294 and not any("#" in leaf.prefix for leaf in child.leaves())
2295 ):
2296 # Drop the now invisible parens so the target splits the same
2297 # way as one written without parentheses. Otherwise the next
2298 # pass, which no longer sees them, can split it differently.
2299 # A tuple target keeps them, since one written without
2300 # parentheses gets invisible parens too.
2301 middle = child.children[1]
2302 middle.prefix = child.prefix
2303 child.replace(middle)
2305 if check_lpar:
2306 if (
2307 child.type == syms.atom
2308 and node.type == syms.for_stmt
2309 and isinstance(child.prev_sibling, Leaf)
2310 and child.prev_sibling.type == token.NAME
2311 and child.prev_sibling.value == "for"
2312 ):
2313 if maybe_make_parens_invisible_in_atom(
2314 child,
2315 parent=node,
2316 mode=mode,
2317 features=features,
2318 remove_brackets_around_comma=True,
2319 ):
2320 wrap_in_parentheses(node, child, visible=False, index=index)
2321 elif isinstance(child, Node) and node.type == syms.with_stmt:
2322 remove_with_parens(child, node, mode=mode, features=features)
2323 elif (
2324 isinstance(child, Node)
2325 and node.type == syms.yield_expr
2326 and child.type == syms.yield_arg
2327 and Preview.parenthesize_tuple_in_yield in mode
2328 ):
2329 if (
2330 len(child.children) == 1
2331 and child.children[0].type != syms.atom
2332 and is_one_tuple(child.children[0])
2333 ):
2334 wrap_in_parentheses(node, child, visible=True, index=index)
2335 elif child.type == syms.atom:
2336 if "in" in parens_after and _is_parenthesized_lambda_or_ternary(child):
2337 # A lambda or conditional expression used as a comprehension's
2338 # iterable must keep at least one pair of parentheses, otherwise
2339 # the trailing `for`/`if` clauses get absorbed into it and the
2340 # code becomes invalid. Any extra nested pairs are redundant, so
2341 # collapse them while keeping exactly one visible pair.
2342 maybe_make_parens_invisible_in_atom(
2343 child, parent=node, mode=mode, features=features
2344 )
2345 opening = child.children[0]
2346 closing = child.children[-1]
2347 if is_lpar_token(opening) and is_rpar_token(closing):
2348 opening.value = "("
2349 closing.value = ")"
2350 elif maybe_make_parens_invisible_in_atom(
2351 child, parent=node, mode=mode, features=features
2352 ):
2353 wrap_in_parentheses(node, child, visible=False, index=index)
2354 elif is_one_tuple(child):
2355 wrap_in_parentheses(node, child, visible=True, index=index)
2356 elif node.type == syms.import_from:
2357 _normalize_import_from(node, child, index)
2358 break
2359 elif (
2360 index == 1
2361 and child.type == token.STAR
2362 and node.type == syms.except_clause
2363 ):
2364 # In except* (PEP 654), the star is actually part of
2365 # of the keyword. So we need to skip the insertion of
2366 # invisible parentheses to work more precisely.
2367 continue
2369 elif (
2370 isinstance(child, Leaf)
2371 and child.next_sibling is not None
2372 and child.next_sibling.type == token.COLON
2373 and child.value == "case"
2374 ):
2375 # A special patch for "case case:" scenario, the second occurrence
2376 # of case will be not parsed as a Python keyword.
2377 break
2379 elif isinstance(child, Node) and child.type == syms.guard:
2380 # Guard nodes handle their own inner wrapping. Wrapping the guard
2381 # itself can produce invalid output when the case pattern splits.
2382 pass
2384 elif not is_multiline_string(child):
2385 if (
2386 Preview.fix_if_guard_explosion_in_case_statement in mode
2387 and node.type == syms.guard
2388 ):
2389 mock_line = Line(mode=mode)
2390 for leaf in child.leaves():
2391 mock_line.append(leaf)
2392 # If it's a guard AND it's short, we DON'T wrap
2393 if not is_line_short_enough(mock_line, mode=mode):
2394 wrap_in_parentheses(node, child, visible=False, index=index)
2395 else:
2396 wrap_in_parentheses(node, child, visible=False, index=index)
2398 comma_check = child.type == token.COMMA
2400 check_lpar = isinstance(child, Leaf) and (
2401 child.value in parens_after or comma_check
2402 )
2405def _normalize_import_from(parent: Node, child: LN, index: int) -> None:
2406 # "import from" nodes store parentheses directly as part of
2407 # the statement
2408 if is_lpar_token(child):
2409 assert is_rpar_token(parent.children[-1])
2410 # make parentheses invisible
2411 child.value = ""
2412 parent.children[-1].value = ""
2413 elif child.type != token.STAR:
2414 # insert invisible parentheses
2415 parent.insert_child(index, Leaf(token.LPAR, ""))
2416 parent.append_child(Leaf(token.RPAR, ""))
2419def remove_await_parens(node: Node, mode: Mode, features: Collection[Feature]) -> None:
2420 if node.children[0].type == token.AWAIT and len(node.children) > 1:
2421 if (
2422 node.children[1].type == syms.atom
2423 and node.children[1].children[0].type == token.LPAR
2424 ):
2425 if maybe_make_parens_invisible_in_atom(
2426 node.children[1],
2427 parent=node,
2428 mode=mode,
2429 features=features,
2430 remove_brackets_around_comma=True,
2431 ):
2432 wrap_in_parentheses(node, node.children[1], visible=False)
2434 # Since await is an expression we shouldn't remove
2435 # brackets in cases where this would change
2436 # the AST due to operator precedence.
2437 # Therefore we only aim to remove brackets around
2438 # power nodes that aren't also await expressions themselves.
2439 # https://peps.python.org/pep-0492/#updated-operator-precedence-table
2440 # N.B. We've still removed any redundant nested brackets though :)
2441 opening_bracket = cast(Leaf, node.children[1].children[0])
2442 closing_bracket = cast(Leaf, node.children[1].children[-1])
2443 bracket_contents = node.children[1].children[1]
2444 if isinstance(bracket_contents, Node) and (
2445 bracket_contents.type != syms.power
2446 or bracket_contents.children[0].type == token.AWAIT
2447 or any(
2448 isinstance(child, Leaf) and child.type == token.DOUBLESTAR
2449 for child in bracket_contents.children
2450 )
2451 ):
2452 ensure_visible(opening_bracket)
2453 ensure_visible(closing_bracket)
2456def _maybe_wrap_cms_in_parens(
2457 node: Node, mode: Mode, features: Collection[Feature]
2458) -> None:
2459 """When enabled and safe, wrap the multiple context managers in invisible parens.
2461 It is only safe when `features` contain Feature.PARENTHESIZED_CONTEXT_MANAGERS.
2462 """
2463 if (
2464 Feature.PARENTHESIZED_CONTEXT_MANAGERS not in features
2465 or len(node.children) <= 2
2466 # If it's an atom, it's already wrapped in parens.
2467 or node.children[1].type == syms.atom
2468 ):
2469 return
2470 colon_index: int | None = None
2471 for i in range(2, len(node.children)):
2472 if node.children[i].type == token.COLON:
2473 colon_index = i
2474 break
2475 if colon_index is not None:
2476 lpar = Leaf(token.LPAR, "")
2477 rpar = Leaf(token.RPAR, "")
2478 context_managers = node.children[1:colon_index]
2479 for child in context_managers:
2480 child.remove()
2481 # After wrapping, the with_stmt will look like this:
2482 # with_stmt
2483 # NAME 'with'
2484 # atom
2485 # LPAR ''
2486 # testlist_gexp
2487 # ... <-- context_managers
2488 # /testlist_gexp
2489 # RPAR ''
2490 # /atom
2491 # COLON ':'
2492 new_child = Node(
2493 syms.atom, [lpar, Node(syms.testlist_gexp, context_managers), rpar]
2494 )
2495 node.insert_child(1, new_child)
2498def remove_with_parens(
2499 node: Node, parent: Node, mode: Mode, features: Collection[Feature]
2500) -> None:
2501 """Recursively hide optional parens in `with` statements."""
2502 # Removing all unnecessary parentheses in with statements in one pass is a tad
2503 # complex as different variations of bracketed statements result in pretty
2504 # different parse trees:
2505 #
2506 # with (open("file")) as f: # this is an asexpr_test
2507 # ...
2508 #
2509 # with (open("file") as f): # this is an atom containing an
2510 # ... # asexpr_test
2511 #
2512 # with (open("file")) as f, (open("file")) as f: # this is asexpr_test, COMMA,
2513 # ... # asexpr_test
2514 #
2515 # with (open("file") as f, open("file") as f): # an atom containing a
2516 # ... # testlist_gexp which then
2517 # # contains multiple asexpr_test(s)
2518 if node.type == syms.atom:
2519 if maybe_make_parens_invisible_in_atom(
2520 node,
2521 parent=parent,
2522 mode=mode,
2523 features=features,
2524 remove_brackets_around_comma=True,
2525 ):
2526 wrap_in_parentheses(parent, node, visible=False)
2527 if isinstance(node.children[1], Node):
2528 remove_with_parens(node.children[1], node, mode=mode, features=features)
2529 elif node.type == syms.testlist_gexp:
2530 for child in node.children:
2531 if isinstance(child, Node):
2532 remove_with_parens(child, node, mode=mode, features=features)
2533 elif node.type == syms.asexpr_test and not any(
2534 leaf.type == token.COLONEQUAL for leaf in node.leaves()
2535 ):
2536 if maybe_make_parens_invisible_in_atom(
2537 node.children[0],
2538 parent=node,
2539 mode=mode,
2540 features=features,
2541 remove_brackets_around_comma=True,
2542 ):
2543 wrap_in_parentheses(node, node.children[0], visible=False)
2546def _atom_has_magic_trailing_comma(node: LN, mode: Mode) -> bool:
2547 """Check if an atom node has a magic trailing comma.
2549 Returns True for single-element tuples with trailing commas like (a,),
2550 which should be preserved to maintain their tuple type.
2551 """
2552 if not mode.magic_trailing_comma:
2553 return False
2555 return is_one_tuple(node)
2558def _is_parenthesized_annotation_target(node: Node, child: Node) -> bool:
2559 """Is `child` a parenthesized plain name that `node` annotates?
2561 `(x): int = 5` and `x: int = 5` do not do the same thing: only the second one
2562 records `x` in `__annotations__`, because the parentheses make the target
2563 non-simple (`AnnAssign.simple` is 0). Dropping them changes what the module
2564 does at runtime, so they have to stay. Attribute and subscript targets are
2565 non-simple either way, so those are left alone here.
2566 """
2567 if len(node.children) < 2:
2568 return False
2570 annassign = node.children[1]
2571 if not isinstance(annassign, Node) or annassign.type != syms.annassign:
2572 return False
2574 # `((x)): int = 5` is non-simple too, so look through any nesting.
2575 target: LN = child
2576 while (
2577 isinstance(target, Node)
2578 and target.type == syms.atom
2579 and len(target.children) == 3
2580 ):
2581 target = target.children[1]
2583 return isinstance(target, Leaf) and target.type == token.NAME
2586def _atom_has_inner_comments(node: LN) -> bool:
2587 """Check if there are comments inside an atom's brackets."""
2588 if not isinstance(node, Node) or len(node.children) < 3:
2589 return False
2591 # The first child's prefix holds comments before the opening bracket.
2592 return any(
2593 "#" in leaf.prefix for child in node.children[1:] for leaf in child.leaves()
2594 )
2597def _is_atom_multiline(node: LN) -> bool:
2598 """Check if an atom node is multiline (indicating intentional formatting)."""
2599 if not isinstance(node, Node) or len(node.children) < 3:
2600 return False
2602 # Check the middle child (between LPAR and RPAR) for newlines in its subtree
2603 # The first child's prefix contains blank lines/comments before the opening paren
2604 middle = node.children[1]
2605 for child in middle.pre_order():
2606 if isinstance(child, Leaf) and "\n" in child.prefix:
2607 return True
2609 return False
2612def maybe_make_parens_invisible_in_atom(
2613 node: LN,
2614 parent: LN,
2615 mode: Mode,
2616 features: Collection[Feature],
2617 remove_brackets_around_comma: bool = False,
2618 allow_star_expr: bool = False,
2619 remove_generator_parens: bool = False,
2620 is_subscript: bool = False,
2621) -> bool:
2622 """If it's safe, make the parens in the atom `node` invisible, recursively.
2623 Additionally, remove repeated, adjacent invisible parens from the atom `node`
2624 as they are redundant.
2626 Returns whether the node should itself be wrapped in invisible parentheses.
2627 """
2628 can_remove_generator_parens = remove_generator_parens and is_generator(node)
2629 if (
2630 node.type not in (syms.atom, syms.expr)
2631 or is_empty_tuple(node)
2632 or (not is_subscript and is_one_tuple(node))
2633 or (is_tuple(node) and parent.type == syms.asexpr_test)
2634 or (
2635 is_tuple(node)
2636 and parent.type == syms.with_stmt
2637 and has_sibling_with_type(node, token.COMMA)
2638 )
2639 or (
2640 is_yield(node)
2641 and parent.type != syms.expr_stmt
2642 and (
2643 parent.type != syms.simple_stmt
2644 or Preview.remove_redundant_top_level_parentheses not in mode
2645 )
2646 )
2647 or (
2648 # This condition tries to prevent removing non-optional brackets
2649 # around a tuple, however, can be a bit overzealous so we provide
2650 # and option to skip this check for `for` and `with` statements.
2651 not remove_brackets_around_comma
2652 and max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY
2653 and not can_remove_generator_parens
2654 # Remove parentheses around multiple exception types in except and
2655 # except* without as. See PEP 758 for details.
2656 and not (
2657 Feature.UNPARENTHESIZED_EXCEPT_TYPES in features
2658 # is a tuple
2659 and is_tuple(node)
2660 # has a parent node
2661 and node.parent is not None
2662 # parent is an except clause
2663 and node.parent.type == syms.except_clause
2664 # is not immediately followed by as clause
2665 and not (
2666 node.next_sibling is not None
2667 and is_name_token(node.next_sibling)
2668 and node.next_sibling.value == "as"
2669 )
2670 )
2671 )
2672 or is_tuple_containing_walrus(node)
2673 or (not allow_star_expr and is_tuple_containing_star(node))
2674 or (not can_remove_generator_parens and is_generator(node))
2675 or (parent.type == syms.comparison and _is_disallowed_comparison_operand(node))
2676 or (
2677 parent.type == syms.simple_stmt and _is_implicitly_concatenated_string(node)
2678 )
2679 ):
2680 return False
2682 if is_walrus_assignment(node):
2683 if parent.type in [
2684 syms.comparison,
2685 syms.annassign,
2686 syms.expr_stmt,
2687 syms.assert_stmt,
2688 syms.return_stmt,
2689 syms.yield_arg,
2690 syms.yield_expr,
2691 syms.except_clause,
2692 syms.funcdef,
2693 syms.with_stmt,
2694 syms.testlist_gexp,
2695 syms.tname,
2696 # these ones aren't useful to end users, but they do please fuzzers
2697 syms.for_stmt,
2698 syms.del_stmt,
2699 syms.simple_stmt,
2700 ]:
2701 return False
2702 if (
2703 parent.type == syms.atom
2704 and parent.children[0].type == token.LBRACE
2705 and Feature.UNPARENTHESIZED_WALRUS_IN_SETS not in features
2706 ):
2707 # A named expression in a set literal needs its parentheses before
2708 # Python 3.10 (3.9.1 accepts it, but 3.9.0 doesn't).
2709 return False
2711 first = node.children[0]
2712 last = node.children[-1]
2713 if is_lpar_token(first) and is_rpar_token(last):
2714 middle = node.children[1]
2715 # make parentheses invisible
2716 if (
2717 # If the prefix of `middle` includes a type comment with
2718 # ignore annotation, then we do not remove the parentheses
2719 not is_type_ignore_comment_string(middle.prefix.strip(), mode=mode)
2720 ):
2721 first.value = ""
2722 last.value = ""
2723 maybe_make_parens_invisible_in_atom(
2724 middle,
2725 parent=parent,
2726 mode=mode,
2727 features=features,
2728 remove_brackets_around_comma=remove_brackets_around_comma,
2729 remove_generator_parens=remove_generator_parens,
2730 is_subscript=is_subscript,
2731 )
2733 if is_atom_with_invisible_parens(middle):
2734 # Strip the invisible parens from `middle` by replacing
2735 # it with the child in-between the invisible parens
2736 middle.replace(middle.children[1])
2738 if middle.children[0].prefix.strip():
2739 # Preserve comments before first paren
2740 middle.children[1].prefix = (
2741 middle.children[0].prefix + middle.children[1].prefix
2742 )
2744 if middle.children[-1].prefix.strip():
2745 # Preserve comments before last paren
2746 last.prefix = middle.children[-1].prefix + last.prefix
2748 return False
2750 return True
2753def should_split_line(line: Line, opening_bracket: Leaf) -> bool:
2754 """Should `line` be immediately split with `delimiter_split()` after RHS?"""
2756 if not (opening_bracket.parent and opening_bracket.value in "[{("):
2757 return False
2759 # We're essentially checking if the body is delimited by commas and there's more
2760 # than one of them (we're excluding the trailing comma and if the delimiter priority
2761 # is still commas, that means there's more).
2762 exclude = set()
2763 trailing_comma = False
2764 try:
2765 last_leaf = line.leaves[-1]
2766 if last_leaf.type == token.COMMA:
2767 trailing_comma = True
2768 exclude.add(id(last_leaf))
2769 max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude)
2770 except (IndexError, ValueError):
2771 return False
2773 return max_priority == COMMA_PRIORITY and (
2774 (line.mode.magic_trailing_comma and trailing_comma)
2775 # always explode imports
2776 or opening_bracket.parent.type in {syms.atom, syms.import_from}
2777 )
2780def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[set[LeafID]]:
2781 """Generate sets of closing bracket IDs that should be omitted in a RHS.
2783 Brackets can be omitted if the entire trailer up to and including
2784 a preceding closing bracket fits in one line.
2786 Yielded sets are cumulative (contain results of previous yields, too). First
2787 set is empty, unless the line should explode, in which case bracket pairs until
2788 the one that needs to explode are omitted.
2789 """
2791 omit: set[LeafID] = set()
2792 if not line.magic_trailing_comma:
2793 yield omit
2795 length = 4 * line.depth
2796 opening_bracket: Leaf | None = None
2797 closing_bracket: Leaf | None = None
2798 inner_brackets: set[LeafID] = set()
2799 for index, leaf, leaf_length in line.enumerate_with_length(is_reversed=True):
2800 length += leaf_length
2801 if length > line_length:
2802 break
2804 has_inline_comment = leaf_length > len(leaf.value) + len(leaf.prefix)
2805 if leaf.type == STANDALONE_COMMENT or has_inline_comment:
2806 break
2808 if opening_bracket:
2809 if leaf is opening_bracket:
2810 opening_bracket = None
2811 elif leaf.type in CLOSING_BRACKETS:
2812 prev = line.leaves[index - 1] if index > 0 else None
2813 if (
2814 prev
2815 and prev.type == token.COMMA
2816 and leaf.opening_bracket is not None
2817 and not is_one_sequence_between(
2818 leaf.opening_bracket, leaf, line.leaves
2819 )
2820 ):
2821 # Never omit bracket pairs with trailing commas.
2822 # We need to explode on those.
2823 break
2825 inner_brackets.add(id(leaf))
2826 elif leaf.type in CLOSING_BRACKETS:
2827 prev = line.leaves[index - 1] if index > 0 else None
2828 if prev and prev.type in OPENING_BRACKETS:
2829 # Empty brackets would fail a split so treat them as "inner"
2830 # brackets (e.g. only add them to the `omit` set if another
2831 # pair of brackets was good enough.
2832 inner_brackets.add(id(leaf))
2833 continue
2835 if closing_bracket:
2836 omit.add(id(closing_bracket))
2837 omit.update(inner_brackets)
2838 inner_brackets.clear()
2839 yield omit
2841 if (
2842 prev
2843 and prev.type == token.COMMA
2844 and leaf.opening_bracket is not None
2845 and not is_one_sequence_between(leaf.opening_bracket, leaf, line.leaves)
2846 ):
2847 # Never omit bracket pairs with trailing commas.
2848 # We need to explode on those.
2849 break
2851 if leaf.value:
2852 opening_bracket = leaf.opening_bracket
2853 closing_bracket = leaf
2856def _over_length_only_due_to_bracket_comment(
2857 line: Line, mode: Mode, allowed_brackets: Collection[int] = OPENING_BRACKETS
2858) -> bool:
2859 """Return True if `line` only exceeds `mode.line_length` because of an inline
2860 comment attached to an opening bracket.
2862 This is the shape produced by issues #4733 and #3681:
2863 a comment inside brackets renders at the end of the head line after Black
2864 splits the statement, pushing it past the limit. If the line without the
2865 comment fits within `mode.line_length`, rejecting an omit-based split does
2866 not prevent the line from exceeding the limit and causes unnecessary
2867 splitting of other trailers (issue #3681).
2868 """
2869 if not line.leaves:
2870 return False
2871 # The over-length must be caused entirely by a trailing comment.
2872 indent = " " * line.depth
2873 leaves_iter = iter(line.leaves)
2874 first = next(leaves_iter)
2875 text_without_comments = f"{first.prefix}{indent}{first.value}"
2876 text_without_comments += "".join(str(leaf) for leaf in leaves_iter)
2877 if str_width(text_without_comments) > mode.line_length:
2878 return False
2879 # And the comment must be attached to an opening bracket.
2880 for leaf_id, comments in line.comments.items():
2881 if not comments:
2882 continue
2883 leaf = next((lf for lf in line.leaves if id(lf) == leaf_id), None)
2884 if leaf is None or leaf.type not in allowed_brackets:
2885 return False
2886 return True
2889def _over_length_only_due_to_subscript_comment(line: Line, mode: Mode) -> bool:
2890 return _over_length_only_due_to_bracket_comment(
2891 line, mode, allowed_brackets={token.LSQB}
2892 )
2895def run_transformer(
2896 line: Line,
2897 transform: Transformer,
2898 mode: Mode,
2899 features: Collection[Feature],
2900 *,
2901 line_str: str = "",
2902) -> list[Line]:
2903 if not line_str:
2904 line_str = line_to_string(line)
2905 # A visible optional paren after the transform means the split already used
2906 # it. Parens nested in the line (around dict values) can turn visible in a
2907 # sub-line's split instead, so they don't count.
2908 optional_parens = [
2909 bracket
2910 for bracket in line.bracket_tracker.invisible
2911 if bracket.bracket_depth == 0
2912 ]
2913 result: list[Line] = []
2914 for transformed_line in transform(line, features, mode):
2915 if str(transformed_line).strip("\n") == line_str:
2916 raise CannotTransform("Line transformer returned an unchanged result")
2918 result.extend(transform_line(transformed_line, mode=mode, features=features))
2920 features_set = set(features)
2921 if (
2922 Feature.FORCE_OPTIONAL_PARENTHESES in features_set
2923 or transform is not right_hand_split_with_omits
2924 or not line.bracket_tracker.invisible
2925 or any(bracket.value for bracket in optional_parens)
2926 or line.contains_multiline_strings()
2927 or result[0].contains_uncollapsable_type_comments()
2928 or result[0].contains_unsplittable_type_ignore()
2929 or is_line_short_enough(result[0], mode=mode)
2930 # result[0] only exceeds the length because of a comment attached to a
2931 # subscript opening bracket. Taking the FORCE_OPTIONAL_PARENTHESES
2932 # "second opinion" wraps the annotation in extra invisible parens and
2933 # migrates the comment outside the subscript, which then oscillates with
2934 # a deeper-bracket split on the next formatter pass (issue #4733).
2935 or _over_length_only_due_to_subscript_comment(result[0], mode)
2936 # If any leaves have no parents (which _can_ occur since
2937 # `transform(line)` potentially destroys the line's underlying node
2938 # structure), then we can't proceed. Doing so would cause the below
2939 # call to `append_leaves()` to fail.
2940 or any(
2941 leaf.parent is None
2942 for leaf in line.leaves
2943 if (
2944 Preview.parenthesize_expressions_with_comments not in mode
2945 or leaf.type != STANDALONE_COMMENT
2946 )
2947 )
2948 ):
2949 return result
2951 line_copy = line.clone()
2952 append_leaves(line_copy, line, line.leaves)
2953 features_fop = features_set | {Feature.FORCE_OPTIONAL_PARENTHESES}
2954 second_opinion = run_transformer(
2955 line_copy, transform, mode, features_fop, line_str=line_str
2956 )
2957 if all(
2958 is_line_short_enough(ln, mode=mode)
2959 for ln in second_opinion
2960 if (
2961 Preview.parenthesize_expressions_with_comments not in mode
2962 or not ln.is_comment
2963 )
2964 ):
2965 result = second_opinion
2966 return result