All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
2 Generating lines of code.
5 from dataclasses import replace
6 from enum import Enum, auto
7 from functools import partial, wraps
8 from typing import Collection, Iterator, List, Optional, Set, Union, cast
10 from black.brackets import (
13 get_leaves_inside_matching_brackets,
14 max_delimiter_priority_in_atom,
16 from black.comments import FMT_OFF, generate_comments, list_comments
17 from black.lines import (
22 can_omit_invisible_parens,
26 from black.mode import Feature, Mode, Preview
27 from black.nodes import (
39 is_async_stmt_or_funcdef,
40 is_atom_with_invisible_parens,
46 is_one_sequence_between,
51 is_tuple_containing_walrus,
58 from black.numerics import normalize_numeric_literal
59 from black.strings import (
62 normalize_string_prefix,
63 normalize_string_quotes,
64 normalize_unicode_escape_sequences,
66 from black.trans import (
75 from blib2to3.pgen2 import token
76 from blib2to3.pytree import Leaf, Node
80 LN = Union[Leaf, Node]
83 class CannotSplit(CannotTransform):
84 """A readable split that fits the allotted line length is impossible."""
87 # This isn't a dataclass because @dataclass + Generic breaks mypyc.
88 # See also https://github.com/mypyc/mypyc/issues/827.
89 class LineGenerator(Visitor[Line]):
90 """Generates reformatted Line objects. Empty lines are not emitted.
92 Note: destroys the tree it's visiting by mutating prefixes of its leaves
93 in ways that will no longer stringify to valid Python code on the tree.
96 def __init__(self, mode: Mode, features: Collection[Feature]) -> None:
98 self.features = features
99 self.current_line: Line
102 def line(self, indent: int = 0) -> Iterator[Line]:
105 If the line is empty, only emit if it makes sense.
106 If the line is too long, split it first and then generate.
108 If any lines were generated, set up a new current_line.
110 if not self.current_line:
111 self.current_line.depth += indent
112 return # Line is empty, don't emit. Creating a new one unnecessary.
115 Preview.improved_async_statements_handling in self.mode
116 and len(self.current_line.leaves) == 1
117 and is_async_stmt_or_funcdef(self.current_line.leaves[0])
119 # Special case for async def/for/with statements. `visit_async_stmt`
120 # adds an `ASYNC` leaf then visits the child def/for/with statement
121 # nodes. Line yields from those nodes shouldn't treat the former
122 # `ASYNC` leaf as a complete line.
125 complete_line = self.current_line
126 self.current_line = Line(mode=self.mode, depth=complete_line.depth + indent)
129 def visit_default(self, node: LN) -> Iterator[Line]:
130 """Default `visit_*()` implementation. Recurses to children of `node`."""
131 if isinstance(node, Leaf):
132 any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
133 for comment in generate_comments(node):
134 if any_open_brackets:
135 # any comment within brackets is subject to splitting
136 self.current_line.append(comment)
137 elif comment.type == token.COMMENT:
138 # regular trailing comment
139 self.current_line.append(comment)
140 yield from self.line()
143 # regular standalone comment
144 yield from self.line()
146 self.current_line.append(comment)
147 yield from self.line()
149 normalize_prefix(node, inside_brackets=any_open_brackets)
150 if self.mode.string_normalization and node.type == token.STRING:
151 node.value = normalize_string_prefix(node.value)
152 node.value = normalize_string_quotes(node.value)
153 if node.type == token.NUMBER:
154 normalize_numeric_literal(node)
155 if node.type not in WHITESPACE:
156 self.current_line.append(node)
157 yield from super().visit_default(node)
159 def visit_test(self, node: Node) -> Iterator[Line]:
160 """Visit an `x if y else z` test"""
162 if Preview.parenthesize_conditional_expressions in self.mode:
163 already_parenthesized = (
164 node.prev_sibling and node.prev_sibling.type == token.LPAR
167 if not already_parenthesized:
168 lpar = Leaf(token.LPAR, "")
169 rpar = Leaf(token.RPAR, "")
170 node.insert_child(0, lpar)
171 node.append_child(rpar)
173 yield from self.visit_default(node)
175 def visit_INDENT(self, node: Leaf) -> Iterator[Line]:
176 """Increase indentation level, maybe yield a line."""
177 # In blib2to3 INDENT never holds comments.
178 yield from self.line(+1)
179 yield from self.visit_default(node)
181 def visit_DEDENT(self, node: Leaf) -> Iterator[Line]:
182 """Decrease indentation level, maybe yield a line."""
183 # The current line might still wait for trailing comments. At DEDENT time
184 # there won't be any (they would be prefixes on the preceding NEWLINE).
185 # Emit the line then.
186 yield from self.line()
188 # While DEDENT has no value, its prefix may contain standalone comments
189 # that belong to the current indentation level. Get 'em.
190 yield from self.visit_default(node)
192 # Finally, emit the dedent.
193 yield from self.line(-1)
196 self, node: Node, keywords: Set[str], parens: Set[str]
198 """Visit a statement.
200 This implementation is shared for `if`, `while`, `for`, `try`, `except`,
201 `def`, `with`, `class`, `assert`, and assignments.
203 The relevant Python language `keywords` for a given statement will be
204 NAME leaves within it. This methods puts those on a separate line.
206 `parens` holds a set of string leaf values immediately after which
207 invisible parens should be put.
209 normalize_invisible_parens(
210 node, parens_after=parens, mode=self.mode, features=self.features
212 for child in node.children:
213 if is_name_token(child) and child.value in keywords:
214 yield from self.line()
216 yield from self.visit(child)
218 def visit_dictsetmaker(self, node: Node) -> Iterator[Line]:
219 if Preview.wrap_long_dict_values_in_parens in self.mode:
220 for i, child in enumerate(node.children):
223 if node.children[i - 1].type == token.COLON:
224 if child.type == syms.atom and child.children[0].type == token.LPAR:
225 if maybe_make_parens_invisible_in_atom(
228 remove_brackets_around_comma=False,
230 wrap_in_parentheses(node, child, visible=False)
232 wrap_in_parentheses(node, child, visible=False)
233 yield from self.visit_default(node)
235 def visit_funcdef(self, node: Node) -> Iterator[Line]:
236 """Visit function definition."""
237 yield from self.line()
239 # Remove redundant brackets around return type annotation.
240 is_return_annotation = False
241 for child in node.children:
242 if child.type == token.RARROW:
243 is_return_annotation = True
244 elif is_return_annotation:
245 if child.type == syms.atom and child.children[0].type == token.LPAR:
246 if maybe_make_parens_invisible_in_atom(
249 remove_brackets_around_comma=False,
251 wrap_in_parentheses(node, child, visible=False)
253 wrap_in_parentheses(node, child, visible=False)
254 is_return_annotation = False
256 for child in node.children:
257 yield from self.visit(child)
259 def visit_match_case(self, node: Node) -> Iterator[Line]:
260 """Visit either a match or case statement."""
261 normalize_invisible_parens(
262 node, parens_after=set(), mode=self.mode, features=self.features
265 yield from self.line()
266 for child in node.children:
267 yield from self.visit(child)
269 def visit_suite(self, node: Node) -> Iterator[Line]:
271 if self.mode.is_pyi and is_stub_suite(node):
272 yield from self.visit(node.children[2])
274 yield from self.visit_default(node)
276 def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
277 """Visit a statement without nested statements."""
278 prev_type: Optional[int] = None
279 for child in node.children:
280 if (prev_type is None or prev_type == token.SEMI) and is_arith_like(child):
281 wrap_in_parentheses(node, child, visible=False)
282 prev_type = child.type
284 is_suite_like = node.parent and node.parent.type in STATEMENT
286 if self.mode.is_pyi and is_stub_body(node):
287 yield from self.visit_default(node)
289 yield from self.line(+1)
290 yield from self.visit_default(node)
291 yield from self.line(-1)
297 or not is_stub_suite(node.parent)
299 yield from self.line()
300 yield from self.visit_default(node)
302 def visit_async_stmt(self, node: Node) -> Iterator[Line]:
303 """Visit `async def`, `async for`, `async with`."""
304 yield from self.line()
306 children = iter(node.children)
307 for child in children:
308 yield from self.visit(child)
310 if child.type == token.ASYNC or child.type == STANDALONE_COMMENT:
311 # STANDALONE_COMMENT happens when `# fmt: skip` is applied on the async
315 internal_stmt = next(children)
316 if Preview.improved_async_statements_handling in self.mode:
317 yield from self.visit(internal_stmt)
319 for child in internal_stmt.children:
320 yield from self.visit(child)
322 def visit_decorators(self, node: Node) -> Iterator[Line]:
323 """Visit decorators."""
324 for child in node.children:
325 yield from self.line()
326 yield from self.visit(child)
328 def visit_power(self, node: Node) -> Iterator[Line]:
329 for idx, leaf in enumerate(node.children[:-1]):
330 next_leaf = node.children[idx + 1]
332 if not isinstance(leaf, Leaf):
335 value = leaf.value.lower()
337 leaf.type == token.NUMBER
338 and next_leaf.type == syms.trailer
339 # Ensure that we are in an attribute trailer
340 and next_leaf.children[0].type == token.DOT
341 # It shouldn't wrap hexadecimal, binary and octal literals
342 and not value.startswith(("0x", "0b", "0o"))
343 # It shouldn't wrap complex literals
346 wrap_in_parentheses(node, leaf)
348 remove_await_parens(node)
350 yield from self.visit_default(node)
352 def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]:
353 """Remove a semicolon and put the other statement on a separate line."""
354 yield from self.line()
356 def visit_ENDMARKER(self, leaf: Leaf) -> Iterator[Line]:
357 """End of file. Process outstanding comments and end with a newline."""
358 yield from self.visit_default(leaf)
359 yield from self.line()
361 def visit_STANDALONE_COMMENT(self, leaf: Leaf) -> Iterator[Line]:
362 if not self.current_line.bracket_tracker.any_open_brackets():
363 yield from self.line()
364 yield from self.visit_default(leaf)
366 def visit_factor(self, node: Node) -> Iterator[Line]:
367 """Force parentheses between a unary op and a binary power:
371 _operator, operand = node.children
373 operand.type == syms.power
374 and len(operand.children) == 3
375 and operand.children[1].type == token.DOUBLESTAR
377 lpar = Leaf(token.LPAR, "(")
378 rpar = Leaf(token.RPAR, ")")
379 index = operand.remove() or 0
380 node.insert_child(index, Node(syms.atom, [lpar, operand, rpar]))
381 yield from self.visit_default(node)
383 def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
384 if Preview.hex_codes_in_unicode_sequences in self.mode:
385 normalize_unicode_escape_sequences(leaf)
387 if is_docstring(leaf) and "\\\n" not in leaf.value:
388 # We're ignoring docstrings with backslash newline escapes because changing
389 # indentation of those changes the AST representation of the code.
390 if self.mode.string_normalization:
391 docstring = normalize_string_prefix(leaf.value)
392 # visit_default() does handle string normalization for us, but
393 # since this method acts differently depending on quote style (ex.
394 # see padding logic below), there's a possibility for unstable
395 # formatting as visit_default() is called *after*. To avoid a
396 # situation where this function formats a docstring differently on
397 # the second pass, normalize it early.
398 docstring = normalize_string_quotes(docstring)
400 docstring = leaf.value
401 prefix = get_string_prefix(docstring)
402 docstring = docstring[len(prefix) :] # Remove the prefix
403 quote_char = docstring[0]
404 # A natural way to remove the outer quotes is to do:
405 # docstring = docstring.strip(quote_char)
406 # but that breaks on """""x""" (which is '""x').
407 # So we actually need to remove the first character and the next two
408 # characters but only if they are the same as the first.
409 quote_len = 1 if docstring[1] != quote_char else 3
410 docstring = docstring[quote_len:-quote_len]
411 docstring_started_empty = not docstring
412 indent = " " * 4 * self.current_line.depth
414 if is_multiline_string(leaf):
415 docstring = fix_docstring(docstring, indent)
417 docstring = docstring.strip()
419 has_trailing_backslash = False
421 # Add some padding if the docstring starts / ends with a quote mark.
422 if docstring[0] == quote_char:
423 docstring = " " + docstring
424 if docstring[-1] == quote_char:
426 if docstring[-1] == "\\":
427 backslash_count = len(docstring) - len(docstring.rstrip("\\"))
428 if backslash_count % 2:
429 # Odd number of tailing backslashes, add some padding to
430 # avoid escaping the closing string quote.
432 has_trailing_backslash = True
433 elif not docstring_started_empty:
436 # We could enforce triple quotes at this point.
437 quote = quote_char * quote_len
439 # It's invalid to put closing single-character quotes on a new line.
440 if self.mode and quote_len == 3:
441 # We need to find the length of the last line of the docstring
442 # to find if we can add the closing quotes to the line without
443 # exceeding the maximum line length.
444 # If docstring is one line, we don't put the closing quotes on a
445 # separate line because it looks ugly (#3320).
446 lines = docstring.splitlines()
447 last_line_length = len(lines[-1]) if docstring else 0
449 # If adding closing quotes would cause the last line to exceed
450 # the maximum line length then put a line break before the
454 and last_line_length + quote_len > self.mode.line_length
455 and len(indent) + quote_len <= self.mode.line_length
456 and not has_trailing_backslash
458 leaf.value = prefix + quote + docstring + "\n" + indent + quote
460 leaf.value = prefix + quote + docstring + quote
462 leaf.value = prefix + quote + docstring + quote
464 yield from self.visit_default(leaf)
466 def __post_init__(self) -> None:
467 """You are in a twisty little maze of passages."""
468 self.current_line = Line(mode=self.mode)
472 self.visit_assert_stmt = partial(v, keywords={"assert"}, parens={"assert", ","})
473 self.visit_if_stmt = partial(
474 v, keywords={"if", "else", "elif"}, parens={"if", "elif"}
476 self.visit_while_stmt = partial(v, keywords={"while", "else"}, parens={"while"})
477 self.visit_for_stmt = partial(v, keywords={"for", "else"}, parens={"for", "in"})
478 self.visit_try_stmt = partial(
479 v, keywords={"try", "except", "else", "finally"}, parens=Ø
481 self.visit_except_clause = partial(v, keywords={"except"}, parens={"except"})
482 self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"})
483 self.visit_classdef = partial(v, keywords={"class"}, parens=Ø)
484 self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS)
485 self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"})
486 self.visit_import_from = partial(v, keywords=Ø, parens={"import"})
487 self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"})
488 self.visit_async_funcdef = self.visit_async_stmt
489 self.visit_decorated = self.visit_decorators
492 self.visit_match_stmt = self.visit_match_case
493 self.visit_case_block = self.visit_match_case
497 line: Line, mode: Mode, features: Collection[Feature] = ()
499 """Transform a `line`, potentially splitting it into many lines.
501 They should fit in the allotted `line_length` but might not be able to.
503 `features` are syntactical features that may be used in the output.
509 line_str = line_to_string(line)
511 ll = mode.line_length
512 sn = mode.string_normalization
513 string_merge = StringMerger(ll, sn)
514 string_paren_strip = StringParenStripper(ll, sn)
515 string_split = StringSplitter(ll, sn)
516 string_paren_wrap = StringParenWrapper(ll, sn)
518 transformers: List[Transformer]
520 not line.contains_uncollapsable_type_comments()
521 and not line.should_split_rhs
522 and not line.magic_trailing_comma
524 is_line_short_enough(line, mode=mode, line_str=line_str)
525 or line.contains_unsplittable_type_ignore()
527 and not (line.inside_brackets and line.contains_standalone_comments())
529 # Only apply basic string preprocessing, since lines shouldn't be split here.
530 if Preview.string_processing in mode:
531 transformers = [string_merge, string_paren_strip]
535 transformers = [left_hand_split]
539 self: object, line: Line, features: Collection[Feature], mode: Mode
541 """Wraps calls to `right_hand_split`.
543 The calls increasingly `omit` right-hand trailers (bracket pairs with
544 content), meaning the trailers get glued together to split on another
545 bracket pair instead.
547 for omit in generate_trailers_to_omit(line, mode.line_length):
548 lines = list(right_hand_split(line, mode, features, omit=omit))
549 # Note: this check is only able to figure out if the first line of the
550 # *current* transformation fits in the line length. This is true only
551 # for simple cases. All others require running more transforms via
552 # `transform_line()`. This check doesn't know if those would succeed.
553 if is_line_short_enough(lines[0], mode=mode):
557 # All splits failed, best effort split with no omits.
558 # This mostly happens to multiline strings that are by definition
559 # reported as not fitting a single line, as well as lines that contain
560 # trailing commas (those have to be exploded).
561 yield from right_hand_split(line, mode, features=features)
563 # HACK: nested functions (like _rhs) compiled by mypyc don't retain their
564 # __name__ attribute which is needed in `run_transformer` further down.
565 # Unfortunately a nested class breaks mypyc too. So a class must be created
566 # via type ... https://github.com/mypyc/mypyc/issues/884
567 rhs = type("rhs", (), {"__call__": _rhs})()
569 if Preview.string_processing in mode:
570 if line.inside_brackets:
576 standalone_comment_split,
589 if line.inside_brackets:
590 transformers = [delimiter_split, standalone_comment_split, rhs]
593 # It's always safe to attempt hugging of power operations and pretty much every line
595 transformers.append(hug_power_op)
597 for transform in transformers:
598 # We are accumulating lines in `result` because we might want to abort
599 # mission and return the original line in the end, or attempt a different
602 result = run_transformer(line, transform, mode, features, line_str=line_str)
603 except CannotTransform:
613 class _BracketSplitComponent(Enum):
620 line: Line, _features: Collection[Feature], mode: Mode
622 """Split line into many lines, starting with the first matching bracket pair.
624 Note: this usually looks weird, only use this for function definitions.
625 Prefer RHS otherwise. This is why this function is not symmetrical with
626 :func:`right_hand_split` which also handles optional parentheses.
628 tail_leaves: List[Leaf] = []
629 body_leaves: List[Leaf] = []
630 head_leaves: List[Leaf] = []
631 current_leaves = head_leaves
632 matching_bracket: Optional[Leaf] = None
633 for leaf in line.leaves:
635 current_leaves is body_leaves
636 and leaf.type in CLOSING_BRACKETS
637 and leaf.opening_bracket is matching_bracket
638 and isinstance(matching_bracket, Leaf)
641 ensure_visible(matching_bracket)
642 current_leaves = tail_leaves if body_leaves else head_leaves
643 current_leaves.append(leaf)
644 if current_leaves is head_leaves:
645 if leaf.type in OPENING_BRACKETS:
646 matching_bracket = leaf
647 current_leaves = body_leaves
648 if not matching_bracket:
649 raise CannotSplit("No brackets found")
651 head = bracket_split_build_line(
652 head_leaves, line, matching_bracket, component=_BracketSplitComponent.head
654 body = bracket_split_build_line(
655 body_leaves, line, matching_bracket, component=_BracketSplitComponent.body
657 tail = bracket_split_build_line(
658 tail_leaves, line, matching_bracket, component=_BracketSplitComponent.tail
660 bracket_split_succeeded_or_raise(head, body, tail)
661 for result in (head, body, tail):
666 def right_hand_split(
669 features: Collection[Feature] = (),
670 omit: Collection[LeafID] = (),
672 """Split line into many lines, starting with the last matching bracket pair.
674 If the split was by optional parentheses, attempt splitting without them, too.
675 `omit` is a collection of closing bracket IDs that shouldn't be considered for
678 Note: running this function modifies `bracket_depth` on the leaves of `line`.
680 rhs_result = _first_right_hand_split(line, omit=omit)
681 yield from _maybe_split_omitting_optional_parens(
682 rhs_result, line, mode, features=features, omit=omit
686 def _first_right_hand_split(
688 omit: Collection[LeafID] = (),
690 """Split the line into head, body, tail starting with the last bracket pair.
692 Note: this function should not have side effects. It's relied upon by
693 _maybe_split_omitting_optional_parens to get an opinion whether to prefer
694 splitting on the right side of an assignment statement.
696 tail_leaves: List[Leaf] = []
697 body_leaves: List[Leaf] = []
698 head_leaves: List[Leaf] = []
699 current_leaves = tail_leaves
700 opening_bracket: Optional[Leaf] = None
701 closing_bracket: Optional[Leaf] = None
702 for leaf in reversed(line.leaves):
703 if current_leaves is body_leaves:
704 if leaf is opening_bracket:
705 current_leaves = head_leaves if body_leaves else tail_leaves
706 current_leaves.append(leaf)
707 if current_leaves is tail_leaves:
708 if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit:
709 opening_bracket = leaf.opening_bracket
710 closing_bracket = leaf
711 current_leaves = body_leaves
712 if not (opening_bracket and closing_bracket and head_leaves):
713 # If there is no opening or closing_bracket that means the split failed and
714 # all content is in the tail. Otherwise, if `head_leaves` are empty, it means
715 # the matching `opening_bracket` wasn't available on `line` anymore.
716 raise CannotSplit("No brackets found")
718 tail_leaves.reverse()
719 body_leaves.reverse()
720 head_leaves.reverse()
721 head = bracket_split_build_line(
722 head_leaves, line, opening_bracket, component=_BracketSplitComponent.head
724 body = bracket_split_build_line(
725 body_leaves, line, opening_bracket, component=_BracketSplitComponent.body
727 tail = bracket_split_build_line(
728 tail_leaves, line, opening_bracket, component=_BracketSplitComponent.tail
730 bracket_split_succeeded_or_raise(head, body, tail)
731 return RHSResult(head, body, tail, opening_bracket, closing_bracket)
734 def _maybe_split_omitting_optional_parens(
738 features: Collection[Feature] = (),
739 omit: Collection[LeafID] = (),
742 Feature.FORCE_OPTIONAL_PARENTHESES not in features
743 # the opening bracket is an optional paren
744 and rhs.opening_bracket.type == token.LPAR
745 and not rhs.opening_bracket.value
746 # the closing bracket is an optional paren
747 and rhs.closing_bracket.type == token.RPAR
748 and not rhs.closing_bracket.value
749 # it's not an import (optional parens are the only thing we can split on
750 # in this case; attempting a split without them is a waste of time)
751 and not line.is_import
752 # there are no standalone comments in the body
753 and not rhs.body.contains_standalone_comments(0)
754 # and we can actually remove the parens
755 and can_omit_invisible_parens(rhs, mode.line_length)
757 omit = {id(rhs.closing_bracket), *omit}
759 # The RHSResult Omitting Optional Parens.
760 rhs_oop = _first_right_hand_split(line, omit=omit)
762 Preview.prefer_splitting_right_hand_side_of_assignments in line.mode
763 # the split is right after `=`
764 and len(rhs.head.leaves) >= 2
765 and rhs.head.leaves[-2].type == token.EQUAL
766 # the left side of assignment contains brackets
767 and any(leaf.type in BRACKETS for leaf in rhs.head.leaves[:-1])
768 # the left side of assignment is short enough (the -1 is for the ending
770 and is_line_short_enough(
771 rhs.head, mode=replace(mode, line_length=mode.line_length - 1)
773 # the left side of assignment won't explode further because of magic
775 and rhs.head.magic_trailing_comma is None
776 # the split by omitting optional parens isn't preferred by some other
778 and not _prefer_split_rhs_oop(rhs_oop, mode)
780 yield from _maybe_split_omitting_optional_parens(
781 rhs_oop, line, mode, features=features, omit=omit
785 except CannotSplit as e:
787 can_be_split(rhs.body) or is_line_short_enough(rhs.body, mode=mode)
790 "Splitting failed, body is still too long and can't be split."
794 rhs.head.contains_multiline_strings()
795 or rhs.tail.contains_multiline_strings()
798 "The current optional pair of parentheses is bound to fail to"
799 " satisfy the splitting algorithm because the head or the tail"
800 " contains multiline strings which by definition never fit one"
804 ensure_visible(rhs.opening_bracket)
805 ensure_visible(rhs.closing_bracket)
806 for result in (rhs.head, rhs.body, rhs.tail):
811 def _prefer_split_rhs_oop(rhs_oop: RHSResult, mode: Mode) -> bool:
813 Returns whether we should prefer the result from a split omitting optional parens.
815 has_closing_bracket_after_assign = False
816 for leaf in reversed(rhs_oop.head.leaves):
817 if leaf.type == token.EQUAL:
819 if leaf.type in CLOSING_BRACKETS:
820 has_closing_bracket_after_assign = True
823 # contains matching brackets after the `=` (done by checking there is a
825 has_closing_bracket_after_assign
827 # the split is actually from inside the optional parens (done by checking
828 # the first line still contains the `=`)
829 any(leaf.type == token.EQUAL for leaf in rhs_oop.head.leaves)
830 # the first line is short enough
831 and is_line_short_enough(rhs_oop.head, mode=mode)
833 # contains unsplittable type ignore
834 or rhs_oop.head.contains_unsplittable_type_ignore()
835 or rhs_oop.body.contains_unsplittable_type_ignore()
836 or rhs_oop.tail.contains_unsplittable_type_ignore()
840 def bracket_split_succeeded_or_raise(head: Line, body: Line, tail: Line) -> None:
841 """Raise :exc:`CannotSplit` if the last left- or right-hand split failed.
843 Do nothing otherwise.
845 A left- or right-hand split is based on a pair of brackets. Content before
846 (and including) the opening bracket is left on one line, content inside the
847 brackets is put on a separate line, and finally content starting with and
848 following the closing bracket is put on a separate line.
850 Those are called `head`, `body`, and `tail`, respectively. If the split
851 produced the same line (all content in `head`) or ended up with an empty `body`
852 and the `tail` is just the closing bracket, then it's considered failed.
854 tail_len = len(str(tail).strip())
857 raise CannotSplit("Splitting brackets produced the same line")
861 f"Splitting brackets on an empty body to save {tail_len} characters is"
866 def bracket_split_build_line(
869 opening_bracket: Leaf,
871 component: _BracketSplitComponent,
873 """Return a new line with given `leaves` and respective comments from `original`.
875 If it's the head component, brackets will be tracked so trailing commas are
878 If it's the body component, the result line is one-indented inside brackets and as
879 such has its first leaf's prefix normalized and a trailing comma added when
882 result = Line(mode=original.mode, depth=original.depth)
883 if component is _BracketSplitComponent.body:
884 result.inside_brackets = True
887 # Since body is a new indent level, remove spurious leading whitespace.
888 normalize_prefix(leaves[0], inside_brackets=True)
889 # Ensure a trailing comma for imports and standalone function arguments, but
890 # be careful not to add one after any comments or within type annotations.
893 and opening_bracket.value == "("
894 and not any(leaf.type == token.COMMA for leaf in leaves)
895 # In particular, don't add one within a parenthesized return annotation.
896 # Unfortunately the indicator we're in a return annotation (RARROW) may
897 # be defined directly in the parent node, the parent of the parent ...
898 # and so on depending on how complex the return annotation is.
899 # This isn't perfect and there's some false negatives but they are in
900 # contexts were a comma is actually fine.
902 node.prev_sibling.type == RARROW
905 getattr(leaves[0].parent, "parent", None),
907 if isinstance(node, Node) and isinstance(node.prev_sibling, Leaf)
911 if original.is_import or no_commas:
912 for i in range(len(leaves) - 1, -1, -1):
913 if leaves[i].type == STANDALONE_COMMENT:
916 if leaves[i].type != token.COMMA:
917 new_comma = Leaf(token.COMMA, ",")
918 leaves.insert(i + 1, new_comma)
921 leaves_to_track: Set[LeafID] = set()
922 if component is _BracketSplitComponent.head:
923 leaves_to_track = get_leaves_inside_matching_brackets(leaves)
929 track_bracket=id(leaf) in leaves_to_track,
931 for comment_after in original.comments_after(leaf):
932 result.append(comment_after, preformatted=True)
933 if component is _BracketSplitComponent.body and should_split_line(
934 result, opening_bracket
936 result.should_split_rhs = True
940 def dont_increase_indentation(split_func: Transformer) -> Transformer:
941 """Normalize prefix of the first leaf in every line returned by `split_func`.
943 This is a decorator over relevant split functions.
948 line: Line, features: Collection[Feature], mode: Mode
950 for split_line in split_func(line, features, mode):
951 normalize_prefix(split_line.leaves[0], inside_brackets=True)
957 def _get_last_non_comment_leaf(line: Line) -> Optional[int]:
958 for leaf_idx in range(len(line.leaves) - 1, 0, -1):
959 if line.leaves[leaf_idx].type != STANDALONE_COMMENT:
964 def _safe_add_trailing_comma(safe: bool, delimiter_priority: int, line: Line) -> Line:
967 and delimiter_priority == COMMA_PRIORITY
968 and line.leaves[-1].type != token.COMMA
969 and line.leaves[-1].type != STANDALONE_COMMENT
971 new_comma = Leaf(token.COMMA, ",")
972 line.append(new_comma)
976 @dont_increase_indentation
978 line: Line, features: Collection[Feature], mode: Mode
980 """Split according to delimiters of the highest priority.
982 If the appropriate Features are given, the split will add trailing commas
983 also in function signatures and calls that contain `*` and `**`.
986 last_leaf = line.leaves[-1]
988 raise CannotSplit("Line empty") from None
990 bt = line.bracket_tracker
992 delimiter_priority = bt.max_delimiter_priority(exclude={id(last_leaf)})
994 raise CannotSplit("No delimiters found") from None
996 if delimiter_priority == DOT_PRIORITY:
997 if bt.delimiter_count_with_priority(delimiter_priority) == 1:
998 raise CannotSplit("Splitting a single attribute from its owner looks wrong")
1000 current_line = Line(
1001 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1003 lowest_depth = sys.maxsize
1004 trailing_comma_safe = True
1006 def append_to_line(leaf: Leaf) -> Iterator[Line]:
1007 """Append `leaf` to current line or to new line if appending impossible."""
1008 nonlocal current_line
1010 current_line.append_safe(leaf, preformatted=True)
1014 current_line = Line(
1015 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1017 current_line.append(leaf)
1019 last_non_comment_leaf = _get_last_non_comment_leaf(line)
1020 for leaf_idx, leaf in enumerate(line.leaves):
1021 yield from append_to_line(leaf)
1023 for comment_after in line.comments_after(leaf):
1024 yield from append_to_line(comment_after)
1026 lowest_depth = min(lowest_depth, leaf.bracket_depth)
1027 if leaf.bracket_depth == lowest_depth:
1028 if is_vararg(leaf, within={syms.typedargslist}):
1029 trailing_comma_safe = (
1030 trailing_comma_safe and Feature.TRAILING_COMMA_IN_DEF in features
1032 elif is_vararg(leaf, within={syms.arglist, syms.argument}):
1033 trailing_comma_safe = (
1034 trailing_comma_safe and Feature.TRAILING_COMMA_IN_CALL in features
1038 Preview.add_trailing_comma_consistently in mode
1039 and last_leaf.type == STANDALONE_COMMENT
1040 and leaf_idx == last_non_comment_leaf
1042 current_line = _safe_add_trailing_comma(
1043 trailing_comma_safe, delimiter_priority, current_line
1046 leaf_priority = bt.delimiters.get(id(leaf))
1047 if leaf_priority == delimiter_priority:
1050 current_line = Line(
1051 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1054 current_line = _safe_add_trailing_comma(
1055 trailing_comma_safe, delimiter_priority, current_line
1060 @dont_increase_indentation
1061 def standalone_comment_split(
1062 line: Line, features: Collection[Feature], mode: Mode
1063 ) -> Iterator[Line]:
1064 """Split standalone comments from the rest of the line."""
1065 if not line.contains_standalone_comments(0):
1066 raise CannotSplit("Line does not have any standalone comments")
1068 current_line = Line(
1069 mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1072 def append_to_line(leaf: Leaf) -> Iterator[Line]:
1073 """Append `leaf` to current line or to new line if appending impossible."""
1074 nonlocal current_line
1076 current_line.append_safe(leaf, preformatted=True)
1080 current_line = Line(
1081 line.mode, depth=line.depth, inside_brackets=line.inside_brackets
1083 current_line.append(leaf)
1085 for leaf in line.leaves:
1086 yield from append_to_line(leaf)
1088 for comment_after in line.comments_after(leaf):
1089 yield from append_to_line(comment_after)
1095 def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
1096 """Leave existing extra newlines if not `inside_brackets`. Remove everything
1099 Note: don't use backslashes for formatting or you'll lose your voting rights.
1101 if not inside_brackets:
1102 spl = leaf.prefix.split("#")
1103 if "\\" not in spl[0]:
1104 nl_count = spl[-1].count("\n")
1107 leaf.prefix = "\n" * nl_count
1113 def normalize_invisible_parens(
1114 node: Node, parens_after: Set[str], *, mode: Mode, features: Collection[Feature]
1116 """Make existing optional parentheses invisible or create new ones.
1118 `parens_after` is a set of string leaf values immediately after which parens
1121 Standardizes on visible parentheses for single-element tuples, and keeps
1122 existing visible parentheses for other tuples and generator expressions.
1124 for pc in list_comments(node.prefix, is_endmarker=False):
1125 if pc.value in FMT_OFF:
1126 # This `node` has a prefix with `# fmt: off`, don't mess with parens.
1129 # The multiple context managers grammar has a different pattern, thus this is
1130 # separate from the for-loop below. This possibly wraps them in invisible parens,
1131 # and later will be removed in remove_with_parens when needed.
1132 if node.type == syms.with_stmt:
1133 _maybe_wrap_cms_in_parens(node, mode, features)
1136 for index, child in enumerate(list(node.children)):
1137 # Fixes a bug where invisible parens are not properly stripped from
1138 # assignment statements that contain type annotations.
1139 if isinstance(child, Node) and child.type == syms.annassign:
1140 normalize_invisible_parens(
1141 child, parens_after=parens_after, mode=mode, features=features
1144 # Add parentheses around long tuple unpacking in assignments.
1147 and isinstance(child, Node)
1148 and child.type == syms.testlist_star_expr
1154 child.type == syms.atom
1155 and node.type == syms.for_stmt
1156 and isinstance(child.prev_sibling, Leaf)
1157 and child.prev_sibling.type == token.NAME
1158 and child.prev_sibling.value == "for"
1160 if maybe_make_parens_invisible_in_atom(
1163 remove_brackets_around_comma=True,
1165 wrap_in_parentheses(node, child, visible=False)
1166 elif isinstance(child, Node) and node.type == syms.with_stmt:
1167 remove_with_parens(child, node)
1168 elif child.type == syms.atom:
1169 if maybe_make_parens_invisible_in_atom(
1173 wrap_in_parentheses(node, child, visible=False)
1174 elif is_one_tuple(child):
1175 wrap_in_parentheses(node, child, visible=True)
1176 elif node.type == syms.import_from:
1177 _normalize_import_from(node, child, index)
1181 and child.type == token.STAR
1182 and node.type == syms.except_clause
1184 # In except* (PEP 654), the star is actually part of
1185 # of the keyword. So we need to skip the insertion of
1186 # invisible parentheses to work more precisely.
1189 elif not (isinstance(child, Leaf) and is_multiline_string(child)):
1190 wrap_in_parentheses(node, child, visible=False)
1192 comma_check = child.type == token.COMMA
1194 check_lpar = isinstance(child, Leaf) and (
1195 child.value in parens_after or comma_check
1199 def _normalize_import_from(parent: Node, child: LN, index: int) -> None:
1200 # "import from" nodes store parentheses directly as part of
1202 if is_lpar_token(child):
1203 assert is_rpar_token(parent.children[-1])
1204 # make parentheses invisible
1206 parent.children[-1].value = ""
1207 elif child.type != token.STAR:
1208 # insert invisible parentheses
1209 parent.insert_child(index, Leaf(token.LPAR, ""))
1210 parent.append_child(Leaf(token.RPAR, ""))
1213 def remove_await_parens(node: Node) -> None:
1214 if node.children[0].type == token.AWAIT and len(node.children) > 1:
1216 node.children[1].type == syms.atom
1217 and node.children[1].children[0].type == token.LPAR
1219 if maybe_make_parens_invisible_in_atom(
1222 remove_brackets_around_comma=True,
1224 wrap_in_parentheses(node, node.children[1], visible=False)
1226 # Since await is an expression we shouldn't remove
1227 # brackets in cases where this would change
1228 # the AST due to operator precedence.
1229 # Therefore we only aim to remove brackets around
1230 # power nodes that aren't also await expressions themselves.
1231 # https://peps.python.org/pep-0492/#updated-operator-precedence-table
1232 # N.B. We've still removed any redundant nested brackets though :)
1233 opening_bracket = cast(Leaf, node.children[1].children[0])
1234 closing_bracket = cast(Leaf, node.children[1].children[-1])
1235 bracket_contents = node.children[1].children[1]
1236 if isinstance(bracket_contents, Node):
1237 if bracket_contents.type != syms.power:
1238 ensure_visible(opening_bracket)
1239 ensure_visible(closing_bracket)
1241 bracket_contents.type == syms.power
1242 and bracket_contents.children[0].type == token.AWAIT
1244 ensure_visible(opening_bracket)
1245 ensure_visible(closing_bracket)
1246 # If we are in a nested await then recurse down.
1247 remove_await_parens(bracket_contents)
1250 def _maybe_wrap_cms_in_parens(
1251 node: Node, mode: Mode, features: Collection[Feature]
1253 """When enabled and safe, wrap the multiple context managers in invisible parens.
1255 It is only safe when `features` contain Feature.PARENTHESIZED_CONTEXT_MANAGERS.
1258 Feature.PARENTHESIZED_CONTEXT_MANAGERS not in features
1259 or Preview.wrap_multiple_context_managers_in_parens not in mode
1260 or len(node.children) <= 2
1261 # If it's an atom, it's already wrapped in parens.
1262 or node.children[1].type == syms.atom
1265 colon_index: Optional[int] = None
1266 for i in range(2, len(node.children)):
1267 if node.children[i].type == token.COLON:
1270 if colon_index is not None:
1271 lpar = Leaf(token.LPAR, "")
1272 rpar = Leaf(token.RPAR, "")
1273 context_managers = node.children[1:colon_index]
1274 for child in context_managers:
1276 # After wrapping, the with_stmt will look like this:
1282 # ... <-- context_managers
1288 syms.atom, [lpar, Node(syms.testlist_gexp, context_managers), rpar]
1290 node.insert_child(1, new_child)
1293 def remove_with_parens(node: Node, parent: Node) -> None:
1294 """Recursively hide optional parens in `with` statements."""
1295 # Removing all unnecessary parentheses in with statements in one pass is a tad
1296 # complex as different variations of bracketed statements result in pretty
1297 # different parse trees:
1299 # with (open("file")) as f: # this is an asexpr_test
1302 # with (open("file") as f): # this is an atom containing an
1305 # with (open("file")) as f, (open("file")) as f: # this is asexpr_test, COMMA,
1308 # with (open("file") as f, open("file") as f): # an atom containing a
1309 # ... # testlist_gexp which then
1310 # # contains multiple asexpr_test(s)
1311 if node.type == syms.atom:
1312 if maybe_make_parens_invisible_in_atom(
1315 remove_brackets_around_comma=True,
1317 wrap_in_parentheses(parent, node, visible=False)
1318 if isinstance(node.children[1], Node):
1319 remove_with_parens(node.children[1], node)
1320 elif node.type == syms.testlist_gexp:
1321 for child in node.children:
1322 if isinstance(child, Node):
1323 remove_with_parens(child, node)
1324 elif node.type == syms.asexpr_test and not any(
1325 leaf.type == token.COLONEQUAL for leaf in node.leaves()
1327 if maybe_make_parens_invisible_in_atom(
1330 remove_brackets_around_comma=True,
1332 wrap_in_parentheses(node, node.children[0], visible=False)
1335 def maybe_make_parens_invisible_in_atom(
1338 remove_brackets_around_comma: bool = False,
1340 """If it's safe, make the parens in the atom `node` invisible, recursively.
1341 Additionally, remove repeated, adjacent invisible parens from the atom `node`
1342 as they are redundant.
1344 Returns whether the node should itself be wrapped in invisible parentheses.
1347 node.type != syms.atom
1348 or is_empty_tuple(node)
1349 or is_one_tuple(node)
1350 or (is_yield(node) and parent.type != syms.expr_stmt)
1352 # This condition tries to prevent removing non-optional brackets
1353 # around a tuple, however, can be a bit overzealous so we provide
1354 # and option to skip this check for `for` and `with` statements.
1355 not remove_brackets_around_comma
1356 and max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY
1358 or is_tuple_containing_walrus(node)
1362 if is_walrus_assignment(node):
1371 # these ones aren't useful to end users, but they do please fuzzers
1378 first = node.children[0]
1379 last = node.children[-1]
1380 if is_lpar_token(first) and is_rpar_token(last):
1381 middle = node.children[1]
1382 # make parentheses invisible
1385 maybe_make_parens_invisible_in_atom(
1388 remove_brackets_around_comma=remove_brackets_around_comma,
1391 if is_atom_with_invisible_parens(middle):
1392 # Strip the invisible parens from `middle` by replacing
1393 # it with the child in-between the invisible parens
1394 middle.replace(middle.children[1])
1401 def should_split_line(line: Line, opening_bracket: Leaf) -> bool:
1402 """Should `line` be immediately split with `delimiter_split()` after RHS?"""
1404 if not (opening_bracket.parent and opening_bracket.value in "[{("):
1407 # We're essentially checking if the body is delimited by commas and there's more
1408 # than one of them (we're excluding the trailing comma and if the delimiter priority
1409 # is still commas, that means there's more).
1411 trailing_comma = False
1413 last_leaf = line.leaves[-1]
1414 if last_leaf.type == token.COMMA:
1415 trailing_comma = True
1416 exclude.add(id(last_leaf))
1417 max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude)
1418 except (IndexError, ValueError):
1421 return max_priority == COMMA_PRIORITY and (
1422 (line.mode.magic_trailing_comma and trailing_comma)
1423 # always explode imports
1424 or opening_bracket.parent.type in {syms.atom, syms.import_from}
1428 def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[LeafID]]:
1429 """Generate sets of closing bracket IDs that should be omitted in a RHS.
1431 Brackets can be omitted if the entire trailer up to and including
1432 a preceding closing bracket fits in one line.
1434 Yielded sets are cumulative (contain results of previous yields, too). First
1435 set is empty, unless the line should explode, in which case bracket pairs until
1436 the one that needs to explode are omitted.
1439 omit: Set[LeafID] = set()
1440 if not line.magic_trailing_comma:
1443 length = 4 * line.depth
1444 opening_bracket: Optional[Leaf] = None
1445 closing_bracket: Optional[Leaf] = None
1446 inner_brackets: Set[LeafID] = set()
1447 for index, leaf, leaf_length in line.enumerate_with_length(reversed=True):
1448 length += leaf_length
1449 if length > line_length:
1452 has_inline_comment = leaf_length > len(leaf.value) + len(leaf.prefix)
1453 if leaf.type == STANDALONE_COMMENT or has_inline_comment:
1457 if leaf is opening_bracket:
1458 opening_bracket = None
1459 elif leaf.type in CLOSING_BRACKETS:
1460 prev = line.leaves[index - 1] if index > 0 else None
1463 and prev.type == token.COMMA
1464 and leaf.opening_bracket is not None
1465 and not is_one_sequence_between(
1466 leaf.opening_bracket, leaf, line.leaves
1469 # Never omit bracket pairs with trailing commas.
1470 # We need to explode on those.
1473 inner_brackets.add(id(leaf))
1474 elif leaf.type in CLOSING_BRACKETS:
1475 prev = line.leaves[index - 1] if index > 0 else None
1476 if prev and prev.type in OPENING_BRACKETS:
1477 # Empty brackets would fail a split so treat them as "inner"
1478 # brackets (e.g. only add them to the `omit` set if another
1479 # pair of brackets was good enough.
1480 inner_brackets.add(id(leaf))
1484 omit.add(id(closing_bracket))
1485 omit.update(inner_brackets)
1486 inner_brackets.clear()
1491 and prev.type == token.COMMA
1492 and leaf.opening_bracket is not None
1493 and not is_one_sequence_between(leaf.opening_bracket, leaf, line.leaves)
1495 # Never omit bracket pairs with trailing commas.
1496 # We need to explode on those.
1500 opening_bracket = leaf.opening_bracket
1501 closing_bracket = leaf
1504 def run_transformer(
1506 transform: Transformer,
1508 features: Collection[Feature],
1513 line_str = line_to_string(line)
1514 result: List[Line] = []
1515 for transformed_line in transform(line, features, mode):
1516 if str(transformed_line).strip("\n") == line_str:
1517 raise CannotTransform("Line transformer returned an unchanged result")
1519 result.extend(transform_line(transformed_line, mode=mode, features=features))
1521 features_set = set(features)
1523 Feature.FORCE_OPTIONAL_PARENTHESES in features_set
1524 or transform.__class__.__name__ != "rhs"
1525 or not line.bracket_tracker.invisible
1526 or any(bracket.value for bracket in line.bracket_tracker.invisible)
1527 or line.contains_multiline_strings()
1528 or result[0].contains_uncollapsable_type_comments()
1529 or result[0].contains_unsplittable_type_ignore()
1530 or is_line_short_enough(result[0], mode=mode)
1531 # If any leaves have no parents (which _can_ occur since
1532 # `transform(line)` potentially destroys the line's underlying node
1533 # structure), then we can't proceed. Doing so would cause the below
1534 # call to `append_leaves()` to fail.
1535 or any(leaf.parent is None for leaf in line.leaves)
1539 line_copy = line.clone()
1540 append_leaves(line_copy, line, line.leaves)
1541 features_fop = features_set | {Feature.FORCE_OPTIONAL_PARENTHESES}
1542 second_opinion = run_transformer(
1543 line_copy, transform, mode, features_fop, line_str=line_str
1545 if all(is_line_short_enough(ln, mode=mode) for ln in second_opinion):
1546 result = second_opinion