From d083472817e7b92e1523bddcca5a220d0924a049 Mon Sep 17 00:00:00 2001 From: jpy-git Date: Sat, 2 Apr 2022 21:11:11 +0100 Subject: [PATCH 1/8] Optional brackets around return anotations --- src/black/linegen.py | 28 ++++++++++++++- .../data/remove_return_annotation_brackets.py | 34 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/data/remove_return_annotation_brackets.py diff --git a/src/black/linegen.py b/src/black/linegen.py index 8a28c3901bb..359c53983df 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -144,6 +144,30 @@ def visit_stmt( yield from self.visit(child) + def visit_funcdef(self, node: Node) -> Iterator[Line]: + """Visit function definition.""" + yield from self.line() + + # Remove redundant brackets around return type annotation. + is_return_annotation = False + for child in node.children: + if child.type == token.RARROW: + is_return_annotation = True + elif is_return_annotation: + if child.type == syms.atom and child.children[0].type == token.LPAR: + if maybe_make_parens_invisible_in_atom( + child, + parent=node, + preview=self.mode.preview, + ): + wrap_in_parentheses(node, child, visible=False) + else: + wrap_in_parentheses(node, child, visible=False) + is_return_annotation = False + + for child in node.children: + yield from self.visit(child) + def visit_match_case(self, node: Node) -> Iterator[Line]: """Visit either a match or case statement.""" normalize_invisible_parens(node, parens_after=set(), preview=self.mode.preview) @@ -325,7 +349,6 @@ def __post_init__(self) -> None: else: self.visit_except_clause = partial(v, keywords={"except"}, parens=Ø) self.visit_with_stmt = partial(v, keywords={"with"}, parens=Ø) - self.visit_funcdef = partial(v, keywords={"def"}, parens=Ø) self.visit_classdef = partial(v, keywords={"class"}, parens=Ø) self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS) self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"}) @@ -477,7 +500,10 @@ def left_hand_split(line: Line, _features: Collection[Feature] = ()) -> Iterator 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: diff --git a/tests/data/remove_return_annotation_brackets.py b/tests/data/remove_return_annotation_brackets.py new file mode 100644 index 00000000000..581bbef8075 --- /dev/null +++ b/tests/data/remove_return_annotation_brackets.py @@ -0,0 +1,34 @@ +# Remove the brackets +def double(a: int) -> (int): + return 2*a + +# Some newline variations +def double(a: int) -> ( + int): + return 2*a + +def double(a: int) -> (int +): + return 2*a + +def double(a: int) -> ( + int +): + return 2*a + +# Don't lose the comments +def double(a: int) -> ( # Hello + int +): + return 2*a + +def double(a: int) -> ( + int # Hello +): + return 2*a + +# Really long annotations +def double() -> ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +): + return 2 From d3ae3c2a15a8d1635d18f6ba291e242f2c79fcd4 Mon Sep 17 00:00:00 2001 From: jpy-git Date: Sat, 2 Apr 2022 23:04:13 +0100 Subject: [PATCH 2/8] Ready to test --- CHANGES.md | 2 + src/black/linegen.py | 39 ++--- src/black/mode.py | 1 + .../data/remove_return_annotation_brackets.py | 34 ----- tests/data/return_annotation_brackets.py | 134 ++++++++++++++++++ tests/test_format.py | 1 + 6 files changed, 159 insertions(+), 52 deletions(-) delete mode 100644 tests/data/remove_return_annotation_brackets.py create mode 100644 tests/data/return_annotation_brackets.py diff --git a/CHANGES.md b/CHANGES.md index f68bc8f1a99..8d809de302e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,8 @@ +- Optional brackets around return anotations (#2990) + ### _Blackd_ diff --git a/src/black/linegen.py b/src/black/linegen.py index 359c53983df..36f5953c21a 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -146,27 +146,30 @@ def visit_stmt( def visit_funcdef(self, node: Node) -> Iterator[Line]: """Visit function definition.""" - yield from self.line() + if Preview.annotation_parens not in self.mode: + self.visit_stmt(node, keywords={"def"}, parens=set()) + else: + yield from self.line() - # Remove redundant brackets around return type annotation. - is_return_annotation = False - for child in node.children: - if child.type == token.RARROW: - is_return_annotation = True - elif is_return_annotation: - if child.type == syms.atom and child.children[0].type == token.LPAR: - if maybe_make_parens_invisible_in_atom( - child, - parent=node, - preview=self.mode.preview, - ): + # Remove redundant brackets around return type annotation. + is_return_annotation = False + for child in node.children: + if child.type == token.RARROW: + is_return_annotation = True + elif is_return_annotation: + if child.type == syms.atom and child.children[0].type == token.LPAR: + if maybe_make_parens_invisible_in_atom( + child, + parent=node, + preview=self.mode.preview, + ): + wrap_in_parentheses(node, child, visible=False) + else: wrap_in_parentheses(node, child, visible=False) - else: - wrap_in_parentheses(node, child, visible=False) - is_return_annotation = False + is_return_annotation = False - for child in node.children: - yield from self.visit(child) + for child in node.children: + yield from self.visit(child) def visit_match_case(self, node: Node) -> Iterator[Line]: """Visit either a match or case statement.""" diff --git a/src/black/mode.py b/src/black/mode.py index 6b74c14b6de..34905702a54 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -129,6 +129,7 @@ class Preview(Enum): string_processing = auto() remove_redundant_parens = auto() one_element_subscript = auto() + annotation_parens = auto() class Deprecated(UserWarning): diff --git a/tests/data/remove_return_annotation_brackets.py b/tests/data/remove_return_annotation_brackets.py deleted file mode 100644 index 581bbef8075..00000000000 --- a/tests/data/remove_return_annotation_brackets.py +++ /dev/null @@ -1,34 +0,0 @@ -# Remove the brackets -def double(a: int) -> (int): - return 2*a - -# Some newline variations -def double(a: int) -> ( - int): - return 2*a - -def double(a: int) -> (int -): - return 2*a - -def double(a: int) -> ( - int -): - return 2*a - -# Don't lose the comments -def double(a: int) -> ( # Hello - int -): - return 2*a - -def double(a: int) -> ( - int # Hello -): - return 2*a - -# Really long annotations -def double() -> ( - intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -): - return 2 diff --git a/tests/data/return_annotation_brackets.py b/tests/data/return_annotation_brackets.py new file mode 100644 index 00000000000..b7408e0a765 --- /dev/null +++ b/tests/data/return_annotation_brackets.py @@ -0,0 +1,134 @@ +# Control +def double(a: int) -> int: + return 2*a + +# Remove the brackets +def double(a: int) -> (int): + return 2*a + +# Some newline variations +def double(a: int) -> ( + int): + return 2*a + +def double(a: int) -> (int +): + return 2*a + +def double(a: int) -> ( + int +): + return 2*a + +# Don't lose the comments +def double(a: int) -> ( # Hello + int +): + return 2*a + +def double(a: int) -> ( + int # Hello +): + return 2*a + +# Really long annotations +def foo() -> ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +): + return 2 + +def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: + return 2 + +def foo() -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: + return 2 + +def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: + return 2 + +def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: + return 2 + +# Split args but no need to split return +def foo(a: int, b: int, c: int,) -> int: + return 2 + +# output +# Control +def double(a: int) -> int: + return 2 * a + + +# Remove the brackets +def double(a: int) -> int: + return 2 * a + + +# Some newline variations +def double(a: int) -> int: + return 2 * a + + +def double(a: int) -> int: + return 2 * a + + +def double(a: int) -> int: + return 2 * a + + +# Don't lose the comments +def double(a: int) -> int: # Hello + return 2 * a + + +def double(a: int) -> int: # Hello + return 2 * a + + +# Really long annotations +def foo() -> ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +): + return 2 + + +def foo() -> ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +): + return 2 + + +def foo() -> ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds + | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +): + return 2 + + +def foo( + a: int, + b: int, + c: int, +) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: + return 2 + + +def foo( + a: int, + b: int, + c: int, +) -> ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds + | intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +): + return 2 + + +# Split args but no need to split return +def foo( + a: int, + b: int, + c: int, +) -> int: + return 2 diff --git a/tests/test_format.py b/tests/test_format.py index a995bd3f1f5..3b6fd5d7f07 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -83,6 +83,7 @@ "remove_except_parens", "remove_for_brackets", "one_element_subscript", + "return_annotation_brackets", ] SOURCES: List[str] = [ From ed6d37403b16e3fa83f01f19678f2efbfdb406a8 Mon Sep 17 00:00:00 2001 From: jpy-git Date: Sat, 2 Apr 2022 23:09:05 +0100 Subject: [PATCH 3/8] whoops missed a yield from --- src/black/linegen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 36f5953c21a..a421fad96f2 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -147,7 +147,7 @@ def visit_stmt( def visit_funcdef(self, node: Node) -> Iterator[Line]: """Visit function definition.""" if Preview.annotation_parens not in self.mode: - self.visit_stmt(node, keywords={"def"}, parens=set()) + yield from self.visit_stmt(node, keywords={"def"}, parens=set()) else: yield from self.line() From 33a1fd6ac92038040b5888b1f449611b4476aa22 Mon Sep 17 00:00:00 2001 From: Joe Young <80432516+jpy-git@users.noreply.github.com> Date: Sat, 2 Apr 2022 23:40:30 +0100 Subject: [PATCH 4/8] Update CHANGES.md Co-authored-by: Jelle Zijlstra --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 75a1426271b..ac6223c4f4b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,7 +14,7 @@ -- Optional brackets around return anotations (#2990) +- Parentheses around return annotations are now managed (#2990) ### _Blackd_ From 81cadaa21a39141cb583b3daa2847ea8913c3ae5 Mon Sep 17 00:00:00 2001 From: jpy-git Date: Sat, 2 Apr 2022 23:48:17 +0100 Subject: [PATCH 5/8] Crazy brackets! --- tests/data/return_annotation_brackets.py | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/data/return_annotation_brackets.py b/tests/data/return_annotation_brackets.py index b7408e0a765..db53a5ac5d6 100644 --- a/tests/data/return_annotation_brackets.py +++ b/tests/data/return_annotation_brackets.py @@ -53,6 +53,27 @@ def foo(a: int, b: int, c: int,) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasd def foo(a: int, b: int, c: int,) -> int: return 2 +# Deeply nested brackets +# with *interesting* spacing +def double(a: int) -> (((((int))))): + return 2*a + +def double(a: int) -> ( + ( ( + ((int) + ) + ) + ) + ): + return 2*a + +def foo() -> ( + ( ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +) +)): + return 2 + # output # Control def double(a: int) -> int: @@ -132,3 +153,19 @@ def foo( c: int, ) -> int: return 2 + + +# Deeply nested brackets +# with *interesting* spacing +def double(a: int) -> int: + return 2 * a + + +def double(a: int) -> int: + return 2 * a + + +def foo() -> ( + intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds +): + return 2 From 86bbff034884da94c8fbcf13779f61d22981684a Mon Sep 17 00:00:00 2001 From: jpy-git Date: Sun, 3 Apr 2022 23:28:51 +0100 Subject: [PATCH 6/8] Fix previous main merge and extra unit tests for commas --- src/black/linegen.py | 2 +- tests/data/return_annotation_brackets.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 057c879a81e..c2b0616d02f 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -161,7 +161,7 @@ def visit_funcdef(self, node: Node) -> Iterator[Line]: if maybe_make_parens_invisible_in_atom( child, parent=node, - preview=self.mode.preview, + remove_brackets_around_comma=False, ): wrap_in_parentheses(node, child, visible=False) else: diff --git a/tests/data/return_annotation_brackets.py b/tests/data/return_annotation_brackets.py index db53a5ac5d6..4a402e92cdf 100644 --- a/tests/data/return_annotation_brackets.py +++ b/tests/data/return_annotation_brackets.py @@ -74,6 +74,15 @@ def foo() -> ( )): return 2 +# Return type with commas +def foo() -> ( + tuple[int, int, int] +): + return 2 + +def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]: + return 2 + # output # Control def double(a: int) -> int: @@ -169,3 +178,18 @@ def foo() -> ( intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds ): return 2 + + +# Return type with commas +def foo() -> tuple[int, int, int]: + return 2 + + +def foo() -> ( + tuple[ + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + ] +): + return 2 From c3a65057237f1c78a35632a2e7d8ee8742d4065c Mon Sep 17 00:00:00 2001 From: jpy-git Date: Sun, 3 Apr 2022 23:31:56 +0100 Subject: [PATCH 7/8] Magic trailing comma example for completeness --- tests/data/return_annotation_brackets.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/data/return_annotation_brackets.py b/tests/data/return_annotation_brackets.py index 4a402e92cdf..265c30220d8 100644 --- a/tests/data/return_annotation_brackets.py +++ b/tests/data/return_annotation_brackets.py @@ -83,6 +83,10 @@ def foo() -> ( def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong]: return 2 +# Magic trailing comma example +def foo() -> tuple[int, int, int,]: + return 2 + # output # Control def double(a: int) -> int: @@ -193,3 +197,14 @@ def foo() -> ( ] ): return 2 + + +# Magic trailing comma example +def foo() -> ( + tuple[ + int, + int, + int, + ] +): + return 2 From fc48804a6d3756ed632f13e6c17e5bb9c5560f5b Mon Sep 17 00:00:00 2001 From: jpy-git Date: Wed, 6 Apr 2022 01:01:33 +0100 Subject: [PATCH 8/8] Turns out this also fixes #2699 :) --- tests/data/return_annotation_brackets.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/data/return_annotation_brackets.py b/tests/data/return_annotation_brackets.py index 265c30220d8..27760bd51d7 100644 --- a/tests/data/return_annotation_brackets.py +++ b/tests/data/return_annotation_brackets.py @@ -87,6 +87,10 @@ def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo def foo() -> tuple[int, int, int,]: return 2 +# Long string example +def frobnicate() -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass + # output # Control def double(a: int) -> int: @@ -208,3 +212,11 @@ def foo() -> ( ] ): return 2 + + +# Long string example +def frobnicate() -> ( + "ThisIsTrulyUnreasonablyExtremelyLongClassName |" + " list[ThisIsTrulyUnreasonablyExtremelyLongClassName]" +): + pass