def remove_await_parens(node: Node) -> None: if node.children[0].type == token.AWAIT and len(node.children) > 1: if ( node.children[1].type == syms.atom and node.children[1].children[0].type == token.LPAR ): if maybe_make_parens_invisible_in_atom( node.children[1], parent=node, remove_brackets_around_comma=True, ): wrap_in_parentheses(node, node.children[1], visible=False) # Since await is an expression we shouldn't remove # brackets in cases where this would change # the AST due to operator precedence. # Therefore we only aim to remove brackets around # power nodes that aren't also await expressions themselves. # https://peps.python.org/pep-0492/#updated-operator-precedence-table # N.B. We've still removed any redundant nested brackets though :) opening_bracket = cast(Leaf, node.children[1].children[0]) closing_bracket = cast(Leaf, node.children[1].children[-1]) bracket_contents = cast(Node, node.children[1].children[1]) if bracket_contents.type != syms.power: ensure_visible(opening_bracket) ensure_visible(closing_bracket) elif ( bracket_contents.type == syms.power and bracket_contents.children[0].type == token.AWAIT ): ensure_visible(opening_bracket) ensure_visible(closing_bracket) # If we are in a nested await then recurse down. remove_await_parens(bracket_contents)
def left_hand_split(line: Line, _features: Collection[Feature] = ()) -> Iterator[Line]: """Split line into many lines, starting with the first matching bracket pair. Note: this usually looks weird, only use this for function definitions. Prefer RHS otherwise. This is why this function is not symmetrical with :func:`right_hand_split` which also handles optional parentheses. """ tail_leaves: List[Leaf] = [] body_leaves: List[Leaf] = [] head_leaves: List[Leaf] = [] current_leaves = head_leaves matching_bracket: Optional[Leaf] = None for leaf in line.leaves: if ( current_leaves is body_leaves and leaf.type in CLOSING_BRACKETS and leaf.opening_bracket is matching_bracket and isinstance(matching_bracket, Leaf) ): ensure_visible(leaf) ensure_visible(matching_bracket) current_leaves = tail_leaves if body_leaves else head_leaves current_leaves.append(leaf) if current_leaves is head_leaves: if leaf.type in OPENING_BRACKETS: matching_bracket = leaf current_leaves = body_leaves if not matching_bracket: raise CannotSplit("No brackets found") head = bracket_split_build_line(head_leaves, line, matching_bracket) body = bracket_split_build_line(body_leaves, line, matching_bracket, is_body=True) tail = bracket_split_build_line(tail_leaves, line, matching_bracket) bracket_split_succeeded_or_raise(head, body, tail) for result in (head, body, tail): if result: yield result
def right_hand_split( line: Line, line_length: int, features: Collection[Feature] = (), omit: Collection[LeafID] = (), ) -> Iterator[Line]: """Split line into many lines, starting with the last matching bracket pair. If the split was by optional parentheses, attempt splitting without them, too. `omit` is a collection of closing bracket IDs that shouldn't be considered for this split. Note: running this function modifies `bracket_depth` on the leaves of `line`. """ tail_leaves: List[Leaf] = [] body_leaves: List[Leaf] = [] head_leaves: List[Leaf] = [] current_leaves = tail_leaves opening_bracket: Optional[Leaf] = None closing_bracket: Optional[Leaf] = None for leaf in reversed(line.leaves): if current_leaves is body_leaves: if leaf is opening_bracket: current_leaves = head_leaves if body_leaves else tail_leaves current_leaves.append(leaf) if current_leaves is tail_leaves: if leaf.type in CLOSING_BRACKETS and id(leaf) not in omit: opening_bracket = leaf.opening_bracket closing_bracket = leaf current_leaves = body_leaves if not (opening_bracket and closing_bracket and head_leaves): # If there is no opening or closing_bracket that means the split failed and # all content is in the tail. Otherwise, if `head_leaves` are empty, it means # the matching `opening_bracket` wasn't available on `line` anymore. raise CannotSplit("No brackets found") tail_leaves.reverse() body_leaves.reverse() head_leaves.reverse() head = bracket_split_build_line(head_leaves, line, opening_bracket) body = bracket_split_build_line(body_leaves, line, opening_bracket, is_body=True) tail = bracket_split_build_line(tail_leaves, line, opening_bracket) bracket_split_succeeded_or_raise(head, body, tail) if ( Feature.FORCE_OPTIONAL_PARENTHESES not in features # the opening bracket is an optional paren and opening_bracket.type == token.LPAR and not opening_bracket.value # the closing bracket is an optional paren and closing_bracket.type == token.RPAR and not closing_bracket.value # it's not an import (optional parens are the only thing we can split on # in this case; attempting a split without them is a waste of time) and not line.is_import # there are no standalone comments in the body and not body.contains_standalone_comments(0) # and we can actually remove the parens and can_omit_invisible_parens(body, line_length) ): omit = {id(closing_bracket), *omit} try: yield from right_hand_split(line, line_length, features=features, omit=omit) return except CannotSplit as e: if not ( can_be_split(body) or is_line_short_enough(body, line_length=line_length) ): raise CannotSplit( "Splitting failed, body is still too long and can't be split." ) from e elif head.contains_multiline_strings() or tail.contains_multiline_strings(): raise CannotSplit( "The current optional pair of parentheses is bound to fail to" " satisfy the splitting algorithm because the head or the tail" " contains multiline strings which by definition never fit one" " line." ) from e ensure_visible(opening_bracket) ensure_visible(closing_bracket) for result in (head, body, tail): if result: yield result