Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Remove parenthesis around sole list items #4312

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

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

- Remove parenthesis around sole list items (#4312)

### Configuration

<!-- Changes to how Black can be configured -->
Expand Down
58 changes: 58 additions & 0 deletions docs/the_black_code_style/future_style.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ The unstable style additionally includes the following features:
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))
- `remove_lone_list_item_parens`: remove redundant parenthesis around lone list items
([see below](labels/lone-list-item-parens))

(labels/hug-parens)=

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

(labels/lone-list-item-parens)=

### Remove keys around sole list item

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

```python
items = [(123)]
items = [(True)]
items = [((True,))]
items = [(())]

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 formatted as:

```python
items = [123]
items = [True]
items = [(True,)]
items = [()]

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
),
]
```

This style depends on `hug_parens_with_braces_and_square_brackets` to remove parenthesis
in all cases.

(labels/string-processing)=

### Improved string processing
Expand Down
13 changes: 13 additions & 0 deletions src/black/linegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,19 @@ def visit_NUMBER(self, leaf: Leaf) -> Iterator[Line]:
normalize_numeric_literal(leaf)
yield from self.visit_default(leaf)

def visit_atom(self, node: Node) -> Iterator[Line]:
"""Visit any atom"""
if (
Preview.remove_lone_list_item_parens 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 visit_fstring(self, node: Node) -> Iterator[Line]:
# currently we don't want to format and split f-strings at all.
string_leaf = fstring_to_string(node)
Expand Down
5 changes: 5 additions & 0 deletions src/black/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ class Preview(Enum):
docstring_check_for_newline = auto()
remove_redundant_guard_parens = auto()
parens_for_long_if_clauses_in_case_block = auto()
# NOTE: remove_lone_list_item_parens requires
# hug_parens_with_braces_and_square_brackets to remove parens in some cases
remove_lone_list_item_parens = auto()


UNSTABLE_FEATURES: Set[Preview] = {
Expand All @@ -217,6 +220,8 @@ class Preview(Enum):
Preview.multiline_string_handling,
# See issue #4036 (crash), #4098, #4099 (proposed tweaks)
Preview.hug_parens_with_braces_and_square_brackets,
# Depends on hug_parens_with_braces_and_square_brackets
Preview.remove_lone_list_item_parens,
}


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 @@ -90,7 +90,8 @@
"is_simple_lookup_for_doublestar_expression",
"docstring_check_for_newline",
"remove_redundant_guard_parens",
"parens_for_long_if_clauses_in_case_block"
"parens_for_long_if_clauses_in_case_block",
"remove_lone_list_item_parens"
]
},
"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
156 changes: 156 additions & 0 deletions tests/data/cases/preview_remove_lone_list_item_parens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# flags: --unstable
items = [(123)]
items = [(True)]
items = [(((((True)))))]
items = [(((((True,)))))]
items = [((((()))))]

items = [
(
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "long strings"
else {"key": "val"}
)
]
items = [
(
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "long strings"
else {"key": "val"}
),
]
items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "long strings"
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 == "long strings"
else "123467890123467890"
)
]

items = [
(
{"key1": "val1", "key2": "val2", "key3": "val3"}
and some_var == "long strings"
and {"key": "val"}
)
]
items = [
(
{"key1": "val1", "key2": "val2", "key3": "val3"}
and some_var == "long strings"
and {"key": "val"}
),
]
items = [
(
"123456890123457890123468901234567890"
and some_var == "long strings"
and "123467890123467890"
)
]
items = [
(
"123456890123457890123468901234567890"
and some_var == "long strings"
and "123467890123467890"
),
]

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 = [123]
items = [True]
items = [True]
items = [(True,)]
items = [()]

items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "long strings"
else {"key": "val"}
]
items = [
(
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "long strings"
else {"key": "val"}
),
]
items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
if some_var == "long strings"
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 == "long strings"
else "123467890123467890"
]

items = [
{"key1": "val1", "key2": "val2", "key3": "val3"}
and some_var == "long strings"
and {"key": "val"}
]
items = [
(
{"key1": "val1", "key2": "val2", "key3": "val3"}
and some_var == "long strings"
and {"key": "val"}
),
]
items = [
"123456890123457890123468901234567890"
and some_var == "long strings"
and "123467890123467890"
]
items = [
(
"123456890123457890123468901234567890"
and some_var == "long strings"
and "123467890123467890"
),
]

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
),
]
Loading