mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 15:14:42 +00:00
Fix joining of f-strings with different quotes when using quote style Preserve
(#15524)
This commit is contained in:
parent
fc9dd63d64
commit
420365811f
3 changed files with 55 additions and 11 deletions
|
@ -11,3 +11,12 @@ a = "different '" 'quote "are fine"' # join
|
||||||
|
|
||||||
# Already invalid Pre Python 312
|
# Already invalid Pre Python 312
|
||||||
f"{'Hy "User"'}" f'{"Hy 'User'"}'
|
f"{'Hy "User"'}" f'{"Hy 'User'"}'
|
||||||
|
|
||||||
|
|
||||||
|
# Regression tests for https://github.com/astral-sh/ruff/issues/15514
|
||||||
|
params = {}
|
||||||
|
string = "this is my string with " f'"{params.get("mine")}"'
|
||||||
|
string = f'"{params.get("mine")} ' f"with {'nested single quoted string'}"
|
||||||
|
string = f"{'''inner ' '''}" f'{"""inner " """}'
|
||||||
|
string = f"{10 + len('bar')=}" f"{10 + len('bar')=}"
|
||||||
|
string = f"{10 + len('bar')=}" f'{10 + len("bar")=}'
|
||||||
|
|
|
@ -44,15 +44,33 @@ impl<'a, 'src> StringNormalizer<'a, 'src> {
|
||||||
let preferred_quote_style = self
|
let preferred_quote_style = self
|
||||||
.preferred_quote_style
|
.preferred_quote_style
|
||||||
.unwrap_or(self.context.options().quote_style());
|
.unwrap_or(self.context.options().quote_style());
|
||||||
|
let supports_pep_701 = self.context.options().target_version().supports_pep_701();
|
||||||
|
|
||||||
|
// For f-strings prefer alternating the quotes unless The outer string is triple quoted and the inner isn't.
|
||||||
|
if let FStringState::InsideExpressionElement(parent_context) = self.context.f_string_state()
|
||||||
|
{
|
||||||
|
let parent_flags = parent_context.f_string().flags();
|
||||||
|
|
||||||
|
if !parent_flags.is_triple_quoted() || string.flags().is_triple_quoted() {
|
||||||
|
// This logic is even necessary when using preserve and the target python version doesn't support PEP701 because
|
||||||
|
// we might end up joining two f-strings that have different quote styles, in which case we need to alternate the quotes
|
||||||
|
// for inner strings to avoid a syntax error: `string = "this is my string with " f'"{params.get("mine")}"'`
|
||||||
|
if !preferred_quote_style.is_preserve() || !supports_pep_701 {
|
||||||
|
return QuoteStyle::from(parent_flags.quote_style().opposite());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leave the quotes unchanged for all other strings.
|
||||||
if preferred_quote_style.is_preserve() {
|
if preferred_quote_style.is_preserve() {
|
||||||
return QuoteStyle::Preserve;
|
return QuoteStyle::Preserve;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There are cases where it is necessary to preserve the quotes to prevent an invalid f-string.
|
||||||
if let StringLikePart::FString(fstring) = string {
|
if let StringLikePart::FString(fstring) = string {
|
||||||
// There are two cases where it's necessary to preserve the quotes if the
|
// There are two cases where it's necessary to preserve the quotes if the
|
||||||
// target version is pre 3.12 and the part is an f-string.
|
// target version is pre 3.12 and the part is an f-string.
|
||||||
if !self.context.options().target_version().supports_pep_701() {
|
if !supports_pep_701 {
|
||||||
// An f-string expression contains a debug text with a quote character
|
// An f-string expression contains a debug text with a quote character
|
||||||
// because the formatter will emit the debug expression **exactly** the
|
// because the formatter will emit the debug expression **exactly** the
|
||||||
// same as in the source text.
|
// same as in the source text.
|
||||||
|
@ -77,16 +95,6 @@ impl<'a, 'src> StringNormalizer<'a, 'src> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For f-strings prefer alternating the quotes unless The outer string is triple quoted and the inner isn't.
|
|
||||||
if let FStringState::InsideExpressionElement(parent_context) = self.context.f_string_state()
|
|
||||||
{
|
|
||||||
let parent_flags = parent_context.f_string().flags();
|
|
||||||
|
|
||||||
if !parent_flags.is_triple_quoted() || string.flags().is_triple_quoted() {
|
|
||||||
return QuoteStyle::from(parent_flags.quote_style().opposite());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Per PEP 8, always prefer double quotes for triple-quoted strings.
|
// Per PEP 8, always prefer double quotes for triple-quoted strings.
|
||||||
if string.flags().is_triple_quoted() {
|
if string.flags().is_triple_quoted() {
|
||||||
// ... unless we're formatting a code snippet inside a docstring,
|
// ... unless we're formatting a code snippet inside a docstring,
|
||||||
|
|
|
@ -17,6 +17,15 @@ a = "different '" 'quote "are fine"' # join
|
||||||
|
|
||||||
# Already invalid Pre Python 312
|
# Already invalid Pre Python 312
|
||||||
f"{'Hy "User"'}" f'{"Hy 'User'"}'
|
f"{'Hy "User"'}" f'{"Hy 'User'"}'
|
||||||
|
|
||||||
|
|
||||||
|
# Regression tests for https://github.com/astral-sh/ruff/issues/15514
|
||||||
|
params = {}
|
||||||
|
string = "this is my string with " f'"{params.get("mine")}"'
|
||||||
|
string = f'"{params.get("mine")} ' f"with {'nested single quoted string'}"
|
||||||
|
string = f"{'''inner ' '''}" f'{"""inner " """}'
|
||||||
|
string = f"{10 + len('bar')=}" f"{10 + len('bar')=}"
|
||||||
|
string = f"{10 + len('bar')=}" f'{10 + len("bar")=}'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
@ -49,6 +58,15 @@ a = "different 'quote \"are fine\"" # join
|
||||||
|
|
||||||
# Already invalid Pre Python 312
|
# Already invalid Pre Python 312
|
||||||
f"{'Hy "User"'}{"Hy 'User'"}"
|
f"{'Hy "User"'}{"Hy 'User'"}"
|
||||||
|
|
||||||
|
|
||||||
|
# Regression tests for https://github.com/astral-sh/ruff/issues/15514
|
||||||
|
params = {}
|
||||||
|
string = f"this is my string with \"{params.get('mine')}\""
|
||||||
|
string = f'"{params.get("mine")} with {"nested single quoted string"}'
|
||||||
|
string = f"{'''inner ' '''}" f'{"""inner " """}'
|
||||||
|
string = f"{10 + len('bar')=}{10 + len('bar')=}"
|
||||||
|
string = f"{10 + len('bar')=}" f'{10 + len("bar")=}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,4 +99,13 @@ a = "different 'quote \"are fine\"" # join
|
||||||
|
|
||||||
# Already invalid Pre Python 312
|
# Already invalid Pre Python 312
|
||||||
f"{'Hy "User"'}{"Hy 'User'"}"
|
f"{'Hy "User"'}{"Hy 'User'"}"
|
||||||
|
|
||||||
|
|
||||||
|
# Regression tests for https://github.com/astral-sh/ruff/issues/15514
|
||||||
|
params = {}
|
||||||
|
string = f"this is my string with \"{params.get("mine")}\""
|
||||||
|
string = f'"{params.get("mine")} with {'nested single quoted string'}'
|
||||||
|
string = f"{'''inner ' '''}{"""inner " """}"
|
||||||
|
string = f"{10 + len('bar')=}{10 + len('bar')=}"
|
||||||
|
string = f"{10 + len('bar')=}{10 + len("bar")=}"
|
||||||
```
|
```
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue