Skip to content

Commit

Permalink
remove redundant parenthesis in `__setitem__/__getitem__ calls
Browse files Browse the repository at this point in the history
  • Loading branch information
KotlinIsland committed May 5, 2023
1 parent eb32729 commit 950ef20
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

- Implicitly concatenated strings used as function args are no longer wrapped inside
parentheses (#3640)
- Redundant parenthesis are removed from `__getitem__`/`__setitem__` calls (#3680)

### Configuration

Expand Down
31 changes: 30 additions & 1 deletion src/black/linegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,30 @@ def visit_power(self, node: Node) -> Iterator[Line]:

yield from self.visit_default(node)

def visit_trailer(self, node: Node) -> Iterator[Line]:
if (
Preview.remove_redundant_parens_setitem_getitem in self.mode
# Ensure that we are in a getitem trailer
and node.children[0].type == token.LSQB
and (
# If there are parens in the getitem
(
node.children[1].type == syms.atom
and node.children[1].children[0].type == token.LPAR
and not is_walrus_assignment(node.children[1])
)
or (
is_walrus_assignment(node.children[1])
# This is only supported in py310 and up
and all(t.value >= 10 for t in self.mode.target_versions)
)
)
):
normalize_invisible_parens(
node, parens_after={"["}, mode=self.mode, features=self.features
)
yield from self.visit_default(node)

def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]:
"""Remove a semicolon and put the other statement on a separate line."""
yield from self.line()
Expand Down Expand Up @@ -1169,6 +1193,8 @@ def normalize_invisible_parens(
if maybe_make_parens_invisible_in_atom(
child,
parent=node,
force_remove_brackets_around_comma=node.type == syms.trailer
and child.children[0].type == token.LPAR,
):
wrap_in_parentheses(node, child, visible=False)
elif is_one_tuple(child):
Expand Down Expand Up @@ -1336,17 +1362,20 @@ def maybe_make_parens_invisible_in_atom(
node: LN,
parent: LN,
remove_brackets_around_comma: bool = False,
force_remove_brackets_around_comma: bool = False,
) -> 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.
"""
if force_remove_brackets_around_comma:
remove_brackets_around_comma = True
if (
node.type != syms.atom
or is_empty_tuple(node)
or is_one_tuple(node)
or (is_one_tuple(node) and not force_remove_brackets_around_comma)
or (is_yield(node) and parent.type != syms.expr_stmt)
or (
# This condition tries to prevent removing non-optional brackets
Expand Down
1 change: 1 addition & 0 deletions src/black/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class Preview(Enum):
skip_magic_trailing_comma_in_subscript = auto()
wrap_long_dict_values_in_parens = auto()
wrap_multiple_context_managers_in_parens = auto()
remove_redundant_parens_setitem_getitem = auto()


class Deprecated(UserWarning):
Expand Down
52 changes: 52 additions & 0 deletions tests/data/miscellaneous/setitem_getitem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
x[1]
x[1,]
x[(1,)]
x[1, 2]
x[1, 2,]
x[(1, 2)]
x[1, 2]
x[(1, 2)]
x[(1, 2),]

x[1] = 1
x[1,] = 1
x[(1,)] = 1
x[1, 2] = 1
x[1, 2,] = 1
x[(1, 2)] = 1
x[1, 2] = 1
x[(1, 2)] = 1
x[(1, 2),] = 1

x[(a := 1)]
x[(a := 1)] = 1

# output
x[1]
x[1,]
x[1,]
x[1, 2]
x[
1,
2,
]
x[1, 2]
x[1, 2]
x[1, 2]
x[(1, 2),]

x[1] = 1
x[1,] = 1
x[1,] = 1
x[1, 2] = 1
x[
1,
2,
] = 1
x[1, 2] = 1
x[1, 2] = 1
x[1, 2] = 1
x[(1, 2),] = 1

x[(a := 1)]
x[(a := 1)] = 1
6 changes: 6 additions & 0 deletions tests/data/miscellaneous/setitem_getitem_py310.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
x[(a := 1)]
x[(a := 1)] = 1

# output
x[a:=1]
x[a:=1] = 1
2 changes: 1 addition & 1 deletion tests/data/preview/percent_precedence.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@
~("" % a)
2 ** ("" % a)
await ("" % a)
b[("" % a)]
b["" % a]
b(("" % a))
12 changes: 12 additions & 0 deletions tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,15 @@ def test_type_comment_syntax_error() -> None:
source, expected = read_data("type_comments", "type_comment_syntax_error")
assert_format(source, expected)
black.assert_equivalent(source, expected)


def test_setitem_getitem() -> None:
source, expected = read_data("miscellaneous", "setitem_getitem")
mode = black.Mode(preview=True, target_versions={black.TargetVersion.PY39})
assert_format(source, expected, mode)


def test_setitem_getitem_py310() -> None:
source, expected = read_data("miscellaneous", "setitem_getitem_py310")
mode = black.Mode(preview=True, target_versions={black.TargetVersion.PY310})
assert_format(source, expected, mode)

0 comments on commit 950ef20

Please sign in to comment.