Skip to content

Commit

Permalink
Avoid magic-trailing-comma in single tuple type
Browse files Browse the repository at this point in the history
  • Loading branch information
jpy-git committed Mar 18, 2022
1 parent fa7f015 commit ea56031
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

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

- Ignore magic comma in one tuple type (#2942)

### _Blackd_

<!-- Changes to blackd -->
Expand Down
27 changes: 23 additions & 4 deletions src/black/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@
from blib2to3.pgen2 import token

from black.brackets import BracketTracker, DOT_PRIORITY
from black.mode import Mode
from black.mode import Mode, Preview
from black.nodes import STANDALONE_COMMENT, TEST_DESCENDANTS
from black.nodes import BRACKETS, OPENING_BRACKETS, CLOSING_BRACKETS
from black.nodes import syms, whitespace, replace_child, child_towards
from black.nodes import is_multiline_string, is_import, is_type_comment
from black.nodes import is_one_tuple_between
from black.nodes import (
is_multiline_string,
is_import,
is_type_comment,
is_within_annotation,
is_one_tuple_between,
)

# types
T = TypeVar("T")
Expand Down Expand Up @@ -253,7 +258,7 @@ def has_magic_trailing_comma(
) -> bool:
"""Return True if we have a magic trailing comma, that is when:
- there's a trailing comma here
- it's not a one-tuple
- it's not a one-tuple (or the equivalent type hint)
Additionally, if ensure_removable:
- it's not from square bracket indexing
"""
Expand All @@ -268,6 +273,20 @@ def has_magic_trailing_comma(
return True

if closing.type == token.RSQB:
if (
Preview.one_tuple_type in self.mode
and is_within_annotation(closing)
and closing.opening_bracket
and is_one_tuple_between(closing.opening_bracket, closing, self.leaves)
and closing.parent
and closing.parent.prev_sibling
and (
list(closing.parent.prev_sibling.leaves())[-1].value
in ("tuple", "Tuple")
)
):
return False

if not ensure_removable:
return True
comma = self.leaves[-1]
Expand Down
5 changes: 2 additions & 3 deletions src/black/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class Preview(Enum):
"""Individual preview style features."""

string_processing = auto()
one_tuple_type = auto()


class Deprecated(UserWarning):
Expand Down Expand Up @@ -162,9 +163,7 @@ def __contains__(self, feature: Preview) -> bool:
"""
if feature is Preview.string_processing:
return self.preview or self.experimental_string_processing
# TODO: Remove type ignore comment once preview contains more features
# than just ESP
return self.preview # type: ignore
return self.preview

def get_cache_key(self) -> str:
if self.target_versions:
Expand Down
17 changes: 16 additions & 1 deletion src/black/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,10 @@ def is_one_tuple(node: LN) -> bool:

def is_one_tuple_between(opening: Leaf, closing: Leaf, leaves: List[Leaf]) -> bool:
"""Return True if content between `opening` and `closing` looks like a one-tuple."""
if opening.type != token.LPAR and closing.type != token.RPAR:
if not (
(opening.type == token.LPAR and closing.type == token.RPAR)
or (opening.type == token.LSQB and closing.type == token.RSQB)
):
return False

depth = closing.bracket_depth + 1
Expand Down Expand Up @@ -597,6 +600,18 @@ def is_walrus_assignment(node: LN) -> bool:
return inner is not None and inner.type == syms.namedexpr_test


def is_within_annotation(node: LN) -> bool:
"""Return True iff `node` is either `annassign` or child of `annassign`"""
found_annassign = False
current_node = node
while current_node.parent:
if current_node.type == syms.annassign:
found_annassign = True
break
current_node = current_node.parent
return found_annassign


def is_simple_decorator_trailer(node: LN, last: bool = False) -> bool:
"""Return True iff `node` is a trailer valid in a simple decorator"""
return node.type == syms.trailer and (
Expand Down
34 changes: 34 additions & 0 deletions tests/data/one_tuple_annotation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import typing
from typing import List, Tuple

# We should not treat the trailing comma
# in a single-element tuple type as a magic comma.
a: tuple[int,]
b: Tuple[int,]
c: typing.Tuple[int,]

# The magic comma still applies to non tuple types.
d: list[int,]
e: List[int,]
f: typing.List[int,]

# output
import typing
from typing import List, Tuple

# We should not treat the trailing comma
# in a single-element tuple type as a magic comma.
a: tuple[int,]
b: Tuple[int,]
c: typing.Tuple[int,]

# The magic comma still applies to non tuple types.
d: list[
int,
]
e: List[
int,
]
f: typing.List[
int,
]
1 change: 1 addition & 0 deletions tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"long_strings__edge_case",
"long_strings__regression",
"percent_precedence",
"one_tuple_annotation",
]

SOURCES: List[str] = [
Expand Down

0 comments on commit ea56031

Please sign in to comment.