from blib2to3.pgen2 import driver, token
from blib2to3.pgen2.parse import ParseError
-__version__ = "18.4a4"
+__version__ = "18.4a6"
DEFAULT_LINE_LENGTH = 88
# types
self.remove_trailing_comma()
return True
- # For parens let's check if it's safe to remove the comma. If the
- # trailing one is the only one, we might mistakenly change a tuple
- # into a different type by removing the comma.
+ # For parens let's check if it's safe to remove the comma.
+ # Imports are always safe.
+ if self.is_import:
+ self.remove_trailing_comma()
+ return True
+
+ # Otheriwsse, if the trailing one is the only one, we might mistakenly
+ # change a tuple into a different type by removing the comma.
depth = closing.bracket_depth + 1
commas = 0
opening = closing.opening_bracket
"""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.
+ Prefer RHS otherwise. This is why this function is not symmetrical with
+ :func:`right_hand_split` which also handles optional parentheses.
"""
head = Line(depth=line.depth)
body = Line(depth=line.depth + 1, inside_brackets=True)
def right_hand_split(
line: Line, py36: bool = False, omit: Collection[LeafID] = ()
) -> Iterator[Line]:
- """Split line into many lines, starting with the last matching bracket pair."""
+ """Split line into many lines, starting with the last matching bracket pair.
+
+ If the split was by optional parentheses, attempt splitting without them, too.
+ """
head = Line(depth=line.depth)
body = Line(depth=line.depth + 1, inside_brackets=True)
tail = Line(depth=line.depth)
bracket_split_succeeded_or_raise(head, body, tail)
assert opening_bracket and closing_bracket
if (
+ # the opening bracket is an optional paren
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
+ # there are no delimiters or standalone comments in the body
+ and not body.bracket_tracker.delimiters
+ and not line.contains_standalone_comments(0)
+ # and 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
):
- # These parens were optional. If there aren't any delimiters or standalone
- # comments in the body, they were unnecessary and another split without
- # them should be attempted.
- if not (
- body.bracket_tracker.delimiters or line.contains_standalone_comments(0)
- ):
- omit = {id(closing_bracket), *omit}
+ omit = {id(closing_bracket), *omit}
+ try:
yield from right_hand_split(line, py36=py36, omit=omit)
return
+ except CannotSplit:
+ pass
ensure_visible(opening_bracket)
ensure_visible(closing_bracket)
for child in list(node.children):
if check_lpar:
if child.type == syms.atom:
- if not (
- is_empty_tuple(child)
- or is_one_tuple(child)
- or max_delimiter_priority_in_atom(child) >= COMMA_PRIORITY
- ):
- first = child.children[0]
- last = child.children[-1]
- if first.type == token.LPAR and last.type == token.RPAR:
- # make parentheses invisible
- first.value = "" # type: ignore
- last.value = "" # type: ignore
+ maybe_make_parens_invisible_in_atom(child)
elif is_one_tuple(child):
# wrap child in visible parentheses
lpar = Leaf(token.LPAR, "(")
check_lpar = isinstance(child, Leaf) and child.value in parens_after
+def maybe_make_parens_invisible_in_atom(node: LN) -> bool:
+ """If it's safe, make the parens in the atom `node` invisible, recusively."""
+ if (
+ node.type != syms.atom
+ or is_empty_tuple(node)
+ or is_one_tuple(node)
+ or max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY
+ ):
+ return False
+
+ first = node.children[0]
+ last = node.children[-1]
+ if first.type == token.LPAR and last.type == token.RPAR:
+ # make parentheses invisible
+ first.value = "" # type: ignore
+ last.value = "" # type: ignore
+ if len(node.children) > 1:
+ maybe_make_parens_invisible_in_atom(node.children[1])
+ return True
+
+ return False
+
+
def is_empty_tuple(node: LN) -> bool:
"""Return True if `node` holds an empty tuple."""
return (
Currently looking for:
- f-strings; and
- - trailing commas after * or ** in function signatures.
+ - trailing commas after * or ** in function signatures and calls.
"""
for n in node.pre_order():
if n.type == token.STRING:
return True
elif (
- n.type == syms.typedargslist
+ n.type in {syms.typedargslist, syms.arglist}
and n.children
and n.children[-1].type == token.COMMA
):
if ch.type in STARS:
return True
+ if ch.type == syms.argument:
+ for argch in ch.children:
+ if argch.type in STARS:
+ return True
+
return False