Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 19 additions & 0 deletions rewrite-python/rewrite/src/rewrite/python/_parser_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2691,6 +2691,25 @@ def visit_Tuple(self, node):
close_line == second_elt.lineno and close_col < second_elt_char_col
):
maybe_parens = False
elif close_paren_idx is not None and len(node.elts) == 1:
# For single-element tuples like `("a"),`, check if the '('
# wraps just the element (grouping parens) vs the tuple.
# If the token after ')' is ',' AND there is no ',' inside
# the parens, then '(' is a grouping paren and the trailing
# ',' outside creates the tuple.
# Compare with `("a",),` where ',' is inside the parens
# (making a proper tuple) and the outer ',' belongs to an
# enclosing context.
next_idx = close_paren_idx + 1
while next_idx < len(self._tokens) and self._tokens[next_idx].type in _SKIP_TOKEN_TYPES:
next_idx += 1
if next_idx < len(self._tokens) and self._tokens[next_idx].string == ',':
# Check if there's a comma inside the parens
prev_idx = close_paren_idx - 1
while prev_idx > self._token_idx and self._tokens[prev_idx].type in _SKIP_TOKEN_TYPES:
prev_idx -= 1
if prev_idx > self._token_idx and self._tokens[prev_idx].string != ',':
maybe_parens = False

if maybe_parens:
self._token_idx += 1 # consume '('
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,23 @@ def test_single_element_tuple_with_trailing_comma():
RecipeSpec().rewrite_run(python("t = (1 , )"))


def test_single_element_tuple_with_trailing_comma_outside_parens():
# language=python - parenthesized expression with trailing comma outside
RecipeSpec().rewrite_run(python('("a"),\n'))


def test_single_element_tuple_with_trailing_comma_outside_parens_multiline():
# language=python - multi-line parenthesized string with trailing comma outside
RecipeSpec().rewrite_run(python(
"""\
(
"Part 1"
" Part 2"
),
"""
))


def test_tuple_with_first_element_in_parens():
# language=python
RecipeSpec().rewrite_run(python("x = (1) // 2, 0"))
Expand Down
10 changes: 10 additions & 0 deletions rewrite-python/rewrite/tests/python/all/tree/type_hint_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ def test_variable_with_parameterized_type_hint_in_quotes():
RecipeSpec().rewrite_run(python("""foo: Dict["Foo", str] = None"""))


def test_literal_string_type_hint_with_assignment():
# language=python - parenthesized string with trailing comma (tuple) before Literal type hint
RecipeSpec().rewrite_run(python(
"""\
("a"),
y: Literal["test"] = "value"
"""
))


def test_variable_with_quoted_type_hint():
# language=python
RecipeSpec().rewrite_run(python("""foo: 'Foo' = None"""))
Expand Down