Skip to content

Commit

Permalink
fix: Remove parenthesis around sole list items
Browse files Browse the repository at this point in the history
Signed-off-by: cobalt <[email protected]>
  • Loading branch information
cobaltt7 committed Apr 17, 2024
1 parent 836acad commit 183a65b
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 12 deletions.
43 changes: 41 additions & 2 deletions docs/the_black_code_style/future_style.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ The unstable style additionally includes the following features:
([see below](labels/wrap-long-dict-values))
- `multiline_string_handling`: more compact formatting of expressions involving
multiline strings ([see below](labels/multiline-string-handling))
- `hug_parens_with_braces_and_square_brackets`: more compact formatting of nested
brackets ([see below](labels/hug-parens))
- `condence_nested_brackets`: more compact formatting of nested brackets
([see below](labels/hug-parens))

(labels/hug-parens)=

Expand Down Expand Up @@ -124,6 +124,45 @@ foo(
)
```

Parenthesises around sole list items will also be removed for similar reasons. Trailing
commas are respected here as well. For example:

```python
items = [
(
long_variable_name
and even_longer_variable_name
and yet_another_very_long_variable_name
)
]

items = [
(
long_variable_name
and even_longer_variable_name
and yet_another_very_long_variable_name
),
]
```

will be changed to:

```python
items = [
long_variable_name
and even_longer_variable_name
and yet_another_very_long_variable_name
]

items = [
(
long_variable_name
and even_longer_variable_name
and yet_another_very_long_variable_name
),
]
```

(labels/string-processing)=

### Improved string processing
Expand Down
29 changes: 24 additions & 5 deletions src/black/linegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,19 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:

yield from self.visit_default(leaf)

def visit_atom(self, node: Node) -> Iterator[Line]:
"""Visit any atom"""
if (
Preview.condence_nested_brackets in self.mode
and len(node.children) == 3
and node.children[0].type == token.LSQB
and node.children[-1].type == token.RSQB
):
# Lists of one item
maybe_make_parens_invisible_in_atom(node.children[1], parent=node)

yield from self.visit_default(node)

def __post_init__(self) -> None:
"""You are in a twisty little maze of passages."""
self.current_line = Line(mode=self.mode)
Expand Down Expand Up @@ -811,7 +824,7 @@ def _first_right_hand_split(

body: Optional[Line] = None
if (
Preview.hug_parens_with_braces_and_square_brackets in line.mode
Preview.condence_nested_brackets in line.mode
and tail_leaves[0].value
and tail_leaves[0].opening_bracket is head_leaves[-1]
):
Expand Down Expand Up @@ -883,19 +896,25 @@ def _maybe_split_omitting_optional_parens(
features: Collection[Feature] = (),
omit: Collection[LeafID] = (),
) -> Iterator[Line]:
if (
both_optional_parens = (
Feature.FORCE_OPTIONAL_PARENTHESES not in features
# the opening bracket is an optional paren
and rhs.opening_bracket.type == token.LPAR
and not rhs.opening_bracket.value
# the closing bracket is an optional paren
and rhs.closing_bracket.type == token.RPAR
and not rhs.closing_bracket.value
)
if (
# If both brackets are optional parens
both_optional_parens
# 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
# 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,
Preview.condence_nested_brackets in mode and line.inside_brackets,
)
):
omit = {id(rhs.closing_bracket), *omit}
try:
Expand Down
6 changes: 4 additions & 2 deletions src/black/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,7 @@ def can_be_split(line: Line) -> bool:
def can_omit_invisible_parens(
rhs: RHSResult,
line_length: int,
inside_brackets: bool,
) -> bool:
"""Does `rhs.body` have a shape safe to reformat without optional parens around it?
Expand Down Expand Up @@ -962,8 +963,9 @@ def can_omit_invisible_parens(
max_priority = bt.max_delimiter_priority()
delimiter_count = bt.delimiter_count_with_priority(max_priority)
if delimiter_count > 1:
# With more than one delimiter of a kind the optional parentheses read better.
return False
# With more than one delimiter of a kind the optional parentheses read
# better, but they're redundant if we're already inside brackets.
return inside_brackets

if delimiter_count == 1:
if max_priority == COMMA_PRIORITY and rhs.head.is_with_or_async_with_stmt:
Expand Down
4 changes: 2 additions & 2 deletions src/black/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class Preview(Enum):
# NOTE: string_processing requires wrap_long_dict_values_in_parens
# for https://github.com/psf/black/issues/3117 to be fixed.
string_processing = auto()
hug_parens_with_braces_and_square_brackets = auto()
condence_nested_brackets = auto()
unify_docstring_detection = auto()
no_normalize_fmt_skip_whitespace = auto()
wrap_long_dict_values_in_parens = auto()
Expand All @@ -191,7 +191,7 @@ class Preview(Enum):
# See issue #4159
Preview.multiline_string_handling,
# See issue #4036 (crash), #4098, #4099 (proposed tweaks)
Preview.hug_parens_with_braces_and_square_brackets,
Preview.condence_nested_brackets,
}


Expand Down
2 changes: 1 addition & 1 deletion src/black/resources/black.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"enum": [
"hex_codes_in_unicode_sequences",
"string_processing",
"hug_parens_with_braces_and_square_brackets",
"condence_nested_brackets",
"unify_docstring_detection",
"no_normalize_fmt_skip_whitespace",
"wrap_long_dict_values_in_parens",
Expand Down
156 changes: 156 additions & 0 deletions tests/data/cases/preview_remove_sole_list_item_parens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# flags: --unstable
items = [
(
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "longstring"
else { "key": "val" }
)
]
items = [
(
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "longstring"
else { "key": "val" }
),
]
items = [(
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "longstring"
else { "key": "val" }
)]
items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "longstring"
else {"key": "val"}
]
items = [
(
{"key1": "val1", "key2": "val2"} if some_var == "longstring" else { "key": "val" }
)
]
items = [(
{"key1": "val1", "key2": "val2"} if some_var == "" else { "key": "val" }
)]
items = [
{"key1": "val1", "key2": "val2"} if some_var == "longstring" else { "key": "val" }
]
items = [
({"key1": "val1", "key2": "val2"} if some_var == "longstring" else { "key": "val" })
]
items = [({"key1": "val1", "key2": "val2"} if some_var == "" else { "key": "val" })]
items = [{"key1": "val1", "key2": "val2"} if some_var == "" else { "key": "val" }]

items = [
(
"123456890123457890123468901234567890"
if some_var == "longstrings"
else "123467890123467890"
)
]
items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
and some_var == "longstring"
and {"key": "val"}
]
items = [
(
"123456890123457890123468901234567890"
and some_var == "longstrings"
and "123467890123467890"
)
]
items = [(
{"key1": "val1", "key2": "val2", "key3": "val3"}
and some_var == "longstring"
and {"key": "val"}
)]


items = [
(
long_variable_name
and even_longer_variable_name
and yet_another_very_long_variable_name
)
]

items = [
(
long_variable_name
and even_longer_variable_name
and yet_another_very_long_variable_name
),
]


# output
items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "longstring"
else {"key": "val"}
]
items = [
(
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "longstring"
else {"key": "val"}
),
]
items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "longstring"
else {"key": "val"}
]
items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "longstring"
else {"key": "val"}
]
items = [
{"key1": "val1", "key2": "val2"} if some_var == "longstring" else {"key": "val"}
]
items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}]
items = [
{"key1": "val1", "key2": "val2"} if some_var == "longstring" else {"key": "val"}
]
items = [
{"key1": "val1", "key2": "val2"} if some_var == "longstring" else {"key": "val"}
]
items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}]
items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}]

items = [
"123456890123457890123468901234567890"
if some_var == "longstrings"
else "123467890123467890"
]
items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
and some_var == "longstring"
and {"key": "val"}
]
items = [
"123456890123457890123468901234567890"
and some_var == "longstrings"
and "123467890123467890"
]
items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
and some_var == "longstring"
and {"key": "val"}
]


items = [
long_variable_name
and even_longer_variable_name
and yet_another_very_long_variable_name
]

items = [
(
long_variable_name
and even_longer_variable_name
and yet_another_very_long_variable_name
),
]

0 comments on commit 183a65b

Please sign in to comment.