From 3dc461a41a13cc36303aff80d079786ef210ddae Mon Sep 17 00:00:00 2001 From: Joe Antonakakis Date: Fri, 23 Aug 2019 07:51:29 -0700 Subject: [PATCH] Fix unstable formatting involving unwrapping multiple parentheses (#836) (#961) This change also unwraps all unnecessary parentheses. --- black.py | 39 ++++++++++++- tests/data/remove_parens.py | 110 ++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 2 deletions(-) diff --git a/black.py b/black.py index 3910392..f178ace 100644 --- a/black.py +++ b/black.py @@ -2882,7 +2882,15 @@ def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None: if is_walrus_assignment(child): continue if child.type == syms.atom: - if maybe_make_parens_invisible_in_atom(child, parent=node): + # Determines if the underlying atom should be surrounded with + # invisible params - also makes parens invisible recursively + # within the atom and removes repeated invisible parens within + # the atom + should_surround_with_parens = maybe_make_parens_invisible_in_atom( + child, parent=node + ) + + if should_surround_with_parens: lpar = Leaf(token.LPAR, "") rpar = Leaf(token.RPAR, "") index = child.remove() or 0 @@ -2999,6 +3007,8 @@ def generate_ignored_nodes(leaf: Leaf) -> Iterator[LN]: def maybe_make_parens_invisible_in_atom(node: LN, parent: LN) -> bool: """If it's safe, make the parens in the atom `node` invisible, recursively. + Additionally, remove repeated, adjacent invisible parens from the atom `node` + as they are redundant. Returns whether the node should itself be wrapped in invisible parentheses. @@ -3015,15 +3025,40 @@ def maybe_make_parens_invisible_in_atom(node: LN, parent: LN) -> bool: first = node.children[0] last = node.children[-1] if first.type == token.LPAR and last.type == token.RPAR: + middle = node.children[1] # make parentheses invisible first.value = "" # type: ignore last.value = "" # type: ignore - maybe_make_parens_invisible_in_atom(node.children[1], parent=parent) + maybe_make_parens_invisible_in_atom(middle, parent=parent) + + if is_atom_with_invisible_parens(middle): + # Strip the invisible parens from `middle` by replacing + # it with the child in-between the invisible parens + middle.replace(middle.children[1]) + return False return True +def is_atom_with_invisible_parens(node: LN) -> bool: + """Given a `LN`, determines whether it's an atom `node` with invisible + parens. Useful in dedupe-ing and normalizing parens. + """ + if isinstance(node, Leaf) or node.type != syms.atom: + return False + + first, last = node.children[0], node.children[-1] + return ( + isinstance(first, Leaf) + and first.type == token.LPAR + and first.value == "" + and isinstance(last, Leaf) + and last.type == token.RPAR + and last.value == "" + ) + + def is_empty_tuple(node: LN) -> bool: """Return True if `node` holds an empty tuple.""" return ( diff --git a/tests/data/remove_parens.py b/tests/data/remove_parens.py index f08f91a..e128f59 100644 --- a/tests/data/remove_parens.py +++ b/tests/data/remove_parens.py @@ -3,8 +3,118 @@ x = (1) x = (1.2) (x) = (3) + +def example(): + return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) + + +def example1(): + return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)) + + +def example1point5(): + return ((((((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111)))))) + + +def example2(): + return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) + + +def example3(): + return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111)) + + +def example4(): + return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((True)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + + +def example5(): + return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + + +def example6(): + return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]}))))))))) + + +def example7(): + return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20000000000000000000]}))))))))) + + +def example8(): + return (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((None))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + + # output print(1) x = 1 x = 1.2 x = 3 + + +def example(): + return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + +def example1(): + return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 + + +def example1point5(): + return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 + + +def example2(): + return ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + ) + + +def example3(): + return ( + 1111111111111111111111111111111111111111111111111111111111111111111111111111111 + ) + + +def example4(): + return True + + +def example5(): + return () + + +def example6(): + return {a: a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]} + + +def example7(): + return { + a: a + for a in [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20000000000000000000, + ] + } + + +def example8(): + return None + -- 2.39.5