Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

<!-- Changes that affect Black's preview style -->

- Fix unnecessary parentheses around short RHS expressions in indexed assignments like
`x[key] = expr` (#5095)
- Prevent string merger from creating unsplittable long lines when a pragma comment
(e.g. `# type: ignore`) follows the closing bracket (#5096)
- Improve heuristics around whether blank lines should appear before, within and after
Expand Down
30 changes: 30 additions & 0 deletions docs/the_black_code_style/future_style.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Currently, the following features are included in the preview style:
- `pyi_overload_group_blank_lines`: In `.pyi` stub files, improve heuristics around when
blank lines should appear before, after and within decorated function groups.
([see below](labels/pyi-overload-group))
- `fix_unnecessary_parens_in_indexed_assignment`: Remove unnecessary parentheses around
the right-hand side of indexed assignments (e.g. `x[key] = expr`) when the expression
is short enough. ([see below](labels/fix-unnecessary-parens-indexed-assignment))

(labels/wrap-comprehension-in)=

Expand Down Expand Up @@ -189,6 +192,33 @@ def foo(x: str) -> str: ...
def bar(x): ...
```

(labels/fix-unnecessary-parens-indexed-assignment)=

### Unnecessary parentheses in indexed assignments

When an assignment target contains brackets (e.g. indexed access like `x[key] = expr`),
Black would previously wrap the right-hand side expression in unnecessary parentheses
when the line was too long. With this feature enabled, Black removes the unnecessary
parentheses when the RHS expression is short enough.
Comment thread
cobaltt7 marked this conversation as resolved.
Outdated

For example:

```python
# Before
dictionary_of_arrays["long_key_name_for_the_example"][
very_long_index_name, index_zero
] = (10 - 5)
```

will be formatted to:

```python
# After (with --preview)
dictionary_of_arrays["long_key_name_for_the_example"][
very_long_index_name, index_zero
] = 10 - 5
```

## Unstable style

(labels/unstable-style)=
Expand Down
2 changes: 1 addition & 1 deletion src/black/linegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,7 @@ def _maybe_split_omitting_optional_parens(
# in this case; attempting a split without them is a waste of time)
and not line.is_import
# and we can actually remove the parens
and can_omit_invisible_parens(rhs, mode.line_length)
and can_omit_invisible_parens(rhs, mode.line_length, mode)
):
omit = {id(rhs.closing_bracket), *omit}
try:
Expand Down
20 changes: 20 additions & 0 deletions src/black/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,7 @@ def can_be_split(line: Line) -> bool:
def can_omit_invisible_parens(
rhs: RHSResult,
line_length: int,
mode: Mode,
) -> bool:
"""Does `rhs.body` have a shape safe to reformat without optional parens around it?

Expand Down Expand Up @@ -1479,6 +1480,25 @@ def can_omit_invisible_parens(
if _can_omit_closing_paren(line, last=last, line_length=line_length):
return True

# For assignment RHS where the LHS contains brackets (e.g. indexed
# assignments like `x[key] = expr`), allow omitting optional parens
# if the body is short enough. The split will happen at the LHS
# brackets instead, and _prefer_split_rhs_oop_over_rhs will decide
# whether it actually produces better output.
if (
Preview.fix_unnecessary_parens_in_indexed_assignment in mode
and len(rhs.head.leaves) >= 2
and rhs.head.leaves[-2].type == token.EQUAL
and any(leaf.type in BRACKETS for leaf in rhs.head.leaves[:-2])
):
# Only when the body is short enough to fit on the tail line
# after the LHS bracket split (e.g. `] = 10 - 5`).
body_length = 4 * line.depth
for _index, _leaf, leaf_length in line.enumerate_with_length():
body_length += leaf_length
if body_length <= line_length // 2:
return True
Comment thread
cobaltt7 marked this conversation as resolved.
Outdated

return False


Expand Down
1 change: 1 addition & 0 deletions src/black/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ class Preview(Enum):
wrap_long_dict_values_in_parens = auto()
fix_if_guard_explosion_in_case_statement = auto()
pyi_overload_group_blank_lines = auto()
fix_unnecessary_parens_in_indexed_assignment = auto()


UNSTABLE_FEATURES: set[Preview] = {
Expand Down
3 changes: 2 additions & 1 deletion src/black/resources/black.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@
"simplify_power_operator_hugging",
"wrap_long_dict_values_in_parens",
"fix_if_guard_explosion_in_case_statement",
"pyi_overload_group_blank_lines"
"pyi_overload_group_blank_lines",
"fix_unnecessary_parens_in_indexed_assignment"
]
},
"description": "Enable specific features included in the `--unstable` style. Requires `--preview`. No compatibility guarantees are provided on the behavior or existence of any unstable features."
Expand Down
21 changes: 21 additions & 0 deletions tests/data/cases/preview_prefer_rhs_split_indexed_assignment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# flags: --preview

# Indexed assignment with a short RHS expression should not get unnecessary parens.
dictionary_of_arrays["long_key_name_for_the_example"][
very_long_index_name, index_zero
] = 10 - 5

# Unformatted input: the unnecessary parens should be removed.
dictionary_of_arrays["long_key_name_for_the_example"][very_long_index_name, index_zero] = (10 - 5)

# output

# Indexed assignment with a short RHS expression should not get unnecessary parens.
dictionary_of_arrays["long_key_name_for_the_example"][
very_long_index_name, index_zero
] = 10 - 5

# Unformatted input: the unnecessary parens should be removed.
dictionary_of_arrays["long_key_name_for_the_example"][
very_long_index_name, index_zero
] = 10 - 5
Loading