Skip to content

Commit

Permalink
Fix bracket spacing for single-element tuples in f-string expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Jan 17, 2025
1 parent e2da33a commit afcf589
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -718,3 +718,11 @@
print(f"{({1, 2, 3}) - ({2})}")
print(f"{1, 2, {3} }")
print(f"{(1, 2, {3})}")

# Regression tests for https://github.com/astral-sh/ruff/issues/15535
print(f"{ {}, }") # A single item tuple gets parenthesized
print(f"{ {}.values(), }")
print(f"{ {}, 1 }") # A tuple with multiple elements doesn't get parenthesized
print(f"{ # Tuple with multiple elements that doesn't fit on a single line gets parenthesized
{}, 1,
}")
33 changes: 22 additions & 11 deletions crates/ruff_python_formatter/src/other/f_string_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,18 +204,13 @@ impl Format<PyFormatContext<'_>> for FormatFStringExpressionElement<'_> {
// before the closing curly brace is not strictly necessary, but it's
// added to maintain consistency.
let bracket_spacing =
match left_most(expression, comments.ranges(), f.context().source()) {
Expr::Dict(_) | Expr::DictComp(_) | Expr::Set(_) | Expr::SetComp(_) => {
Some(format_with(|f| {
if self.context.can_contain_line_breaks() {
soft_line_break_or_space().fmt(f)
} else {
space().fmt(f)
}
}))
needs_bracket_spacing(expression, f.context()).then_some(format_with(|f| {
if self.context.can_contain_line_breaks() {
soft_line_break_or_space().fmt(f)
} else {
space().fmt(f)
}
_ => None,
};
}));

let item = format_with(|f: &mut PyFormatter| {
// Update the context to be inside the f-string expression element.
Expand Down Expand Up @@ -290,3 +285,19 @@ impl Format<PyFormatContext<'_>> for FormatFStringExpressionElement<'_> {
}
}
}

fn needs_bracket_spacing(expr: &Expr, context: &PyFormatContext) -> bool {
// Ruff parenthesizes single element tuples, that's why we shouldn't insert
// a space around the curly braces for those.
if expr
.as_tuple_expr()
.is_some_and(|tuple| !tuple.parenthesized && tuple.elts.len() == 1)
{
return false;
}

matches!(
left_most(expr, context.comments().ranges(), context.source()),
Expr::Dict(_) | Expr::DictComp(_) | Expr::Set(_) | Expr::SetComp(_)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,14 @@ print(f"{ {1: 2}.keys() }")
print(f"{({1, 2, 3}) - ({2})}")
print(f"{1, 2, {3} }")
print(f"{(1, 2, {3})}")

# Regression tests for https://github.com/astral-sh/ruff/issues/15535
print(f"{ {}, }") # A single item tuple gets parenthesized
print(f"{ {}.values(), }")
print(f"{ {}, 1 }") # A tuple with multiple elements doesn't get parenthesized
print(f"{ # Tuple with multiple elements that doesn't fit on a single line gets parenthesized
{}, 1,
}")
```
## Outputs
Expand Down Expand Up @@ -1506,6 +1514,19 @@ print(f"{ {1: 2}.keys() }")
print(f"{({1, 2, 3}) - ({2})}")
print(f"{1, 2, {3}}")
print(f"{(1, 2, {3})}")

# Regression tests for https://github.com/astral-sh/ruff/issues/15535
print(f"{({},)}") # A single item tuple gets parenthesized
print(f"{({}.values(),)}")
print(f"{ {}, 1 }") # A tuple with multiple elements doesn't get parenthesized
print(
f"{ # Tuple with multiple elements that doesn't fit on a single line gets parenthesized
(
{},
1,
)
}"
)
```
Expand Down Expand Up @@ -2288,4 +2309,17 @@ print(f"{ {1: 2}.keys() }")
print(f"{({1, 2, 3}) - ({2})}")
print(f"{1, 2, {3}}")
print(f"{(1, 2, {3})}")

# Regression tests for https://github.com/astral-sh/ruff/issues/15535
print(f"{({},)}") # A single item tuple gets parenthesized
print(f"{({}.values(),)}")
print(f"{ {}, 1 }") # A tuple with multiple elements doesn't get parenthesized
print(
f"{ # Tuple with multiple elements that doesn't fit on a single line gets parenthesized
(
{},
1,
)
}"
)
```

0 comments on commit afcf589

Please sign in to comment.