mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
Hug closing }
when f-string expression has a format specifier (#18704)
This commit is contained in:
parent
2b731d19b9
commit
c22f809049
11 changed files with 323 additions and 198 deletions
|
@ -242,18 +242,22 @@ f"{ # comment 15
|
||||||
}" # comment 19
|
}" # comment 19
|
||||||
# comment 20
|
# comment 20
|
||||||
|
|
||||||
# Single-quoted f-strings with a format specificer can be multiline
|
# The specifier of an f-string must hug the closing `}` because a multiline format specifier is invalid syntax in a single
|
||||||
f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
# quoted f-string.
|
||||||
variable:.3f} ddddddddddddddd eeeeeeee"
|
f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable
|
||||||
|
:.3f} ddddddddddddddd eeeeeeee"
|
||||||
|
|
||||||
# But, if it's triple-quoted then we can't or the format specificer will have a
|
# The same applies for triple quoted f-strings, except that we need to preserve the newline before the closing `}`.
|
||||||
# trailing newline
|
# or we risk altering the meaning of the f-string.
|
||||||
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable
|
||||||
variable:.3f} ddddddddddddddd eeeeeeee"""
|
:.3f} ddddddddddddddd eeeeeeee"""
|
||||||
|
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f
|
||||||
|
} ddddddddddddddd eeeeeeee"""
|
||||||
|
|
||||||
# But, we can break the ones which don't have a format specifier
|
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
f"""fooooooooooooooooooo barrrrrrrrrrrrrrrrrrr {
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd
|
||||||
xxxxxxxxxxxxxxx:.3f} aaaaaaaaaaaaaaaaa { xxxxxxxxxxxxxxxxxxxx } bbbbbbbbbbbb"""
|
# comment
|
||||||
|
:.3f} cccccccccc"""
|
||||||
|
|
||||||
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
||||||
# which is part of the format specifier
|
# which is part of the format specifier
|
||||||
|
@ -285,6 +289,13 @@ x = f"{x !s
|
||||||
# comment 21
|
# comment 21
|
||||||
}"
|
}"
|
||||||
|
|
||||||
|
x = f"{
|
||||||
|
x!s:>{
|
||||||
|
0
|
||||||
|
# comment 21-2
|
||||||
|
}}"
|
||||||
|
|
||||||
|
|
||||||
x = f"""
|
x = f"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
@ -309,6 +320,21 @@ f"{ # comment 26
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"
|
||||||
|
|
||||||
|
|
||||||
|
f"""{foo
|
||||||
|
:a{
|
||||||
|
a # comment 29
|
||||||
|
# comment 30
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/18672
|
||||||
|
f"{
|
||||||
|
# comment 31
|
||||||
|
foo
|
||||||
|
:>
|
||||||
|
}"
|
||||||
|
|
||||||
# Assignment statement
|
# Assignment statement
|
||||||
|
|
||||||
# Even though this f-string has multiline expression, thus allowing us to break it at the
|
# Even though this f-string has multiline expression, thus allowing us to break it at the
|
||||||
|
|
|
@ -240,18 +240,20 @@ t"{ # comment 15
|
||||||
}" # comment 19
|
}" # comment 19
|
||||||
# comment 20
|
# comment 20
|
||||||
|
|
||||||
# Single-quoted t-strings with a format specificer can be multiline
|
# The specifier of a t-string must hug the closing `}` because a multiline format specifier is invalid syntax in a single
|
||||||
|
# quoted f-string.
|
||||||
t"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
t"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
variable:.3f} ddddddddddddddd eeeeeeee"
|
variable
|
||||||
|
:.3f} ddddddddddddddd eeeeeeee"
|
||||||
|
|
||||||
# But, if it's triple-quoted then we can't or the format specificer will have a
|
# The same applies for triple quoted t-strings, except that we need to preserve the newline before the closing `}`.
|
||||||
# trailing newline
|
# or we risk altering the meaning of the f-string.
|
||||||
t"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
t"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable
|
||||||
variable:.3f} ddddddddddddddd eeeeeeee"""
|
:.3f} ddddddddddddddd eeeeeeee"""
|
||||||
|
t"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable
|
||||||
|
:.3f
|
||||||
|
} ddddddddddddddd eeeeeeee"""
|
||||||
|
|
||||||
# But, we can break the ones which don't have a format specifier
|
|
||||||
t"""fooooooooooooooooooo barrrrrrrrrrrrrrrrrrr {
|
|
||||||
xxxxxxxxxxxxxxx:.3f} aaaaaaaaaaaaaaaaa { xxxxxxxxxxxxxxxxxxxx } bbbbbbbbbbbb"""
|
|
||||||
|
|
||||||
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
||||||
# which is part of the format specifier
|
# which is part of the format specifier
|
||||||
|
@ -283,6 +285,12 @@ x = t"{x !s
|
||||||
# comment 21
|
# comment 21
|
||||||
}"
|
}"
|
||||||
|
|
||||||
|
x = f"{
|
||||||
|
x!s:>{
|
||||||
|
0
|
||||||
|
# comment 21-2
|
||||||
|
}}"
|
||||||
|
|
||||||
x = t"""
|
x = t"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
|
|
@ -321,30 +321,35 @@ fn handle_enclosed_comment<'a>(
|
||||||
},
|
},
|
||||||
AnyNodeRef::FString(fstring) => CommentPlacement::dangling(fstring, comment),
|
AnyNodeRef::FString(fstring) => CommentPlacement::dangling(fstring, comment),
|
||||||
AnyNodeRef::TString(tstring) => CommentPlacement::dangling(tstring, comment),
|
AnyNodeRef::TString(tstring) => CommentPlacement::dangling(tstring, comment),
|
||||||
AnyNodeRef::InterpolatedElement(_) => {
|
AnyNodeRef::InterpolatedElement(element) => {
|
||||||
// Handle comments after the format specifier (should be rare):
|
if let Some(preceding) = comment.preceding_node() {
|
||||||
//
|
if comment.line_position().is_own_line() && element.format_spec.is_some() {
|
||||||
// ```python
|
return if comment.following_node().is_some() {
|
||||||
// f"literal {
|
// Own line comment before format specifier
|
||||||
// expr:.3f
|
// ```py
|
||||||
|
// aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
|
// aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd
|
||||||
// # comment
|
// # comment
|
||||||
|
// :.3f} cccccccccc"""
|
||||||
|
// ```
|
||||||
|
CommentPlacement::trailing(preceding, comment)
|
||||||
|
} else {
|
||||||
|
// TODO: This can be removed once format specifiers with a newline are a syntax error.
|
||||||
|
// This is to handle cases like:
|
||||||
|
// ```py
|
||||||
|
// x = f"{x !s
|
||||||
|
// :>0
|
||||||
|
// # comment 21
|
||||||
// }"
|
// }"
|
||||||
// ```
|
// ```
|
||||||
//
|
CommentPlacement::trailing(element, comment)
|
||||||
// This is a valid comment placement.
|
};
|
||||||
if matches!(
|
|
||||||
comment.preceding_node(),
|
|
||||||
Some(
|
|
||||||
AnyNodeRef::InterpolatedElement(_)
|
|
||||||
| AnyNodeRef::InterpolatedStringLiteralElement(_)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
CommentPlacement::trailing(comment.enclosing_node(), comment)
|
|
||||||
} else {
|
|
||||||
handle_bracketed_end_of_line_comment(comment, source)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle_bracketed_end_of_line_comment(comment, source)
|
||||||
|
}
|
||||||
|
|
||||||
AnyNodeRef::ExprList(_)
|
AnyNodeRef::ExprList(_)
|
||||||
| AnyNodeRef::ExprSet(_)
|
| AnyNodeRef::ExprSet(_)
|
||||||
| AnyNodeRef::ExprListComp(_)
|
| AnyNodeRef::ExprListComp(_)
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ruff_python_parser::Tokens;
|
||||||
|
|
||||||
use crate::PyFormatOptions;
|
use crate::PyFormatOptions;
|
||||||
use crate::comments::Comments;
|
use crate::comments::Comments;
|
||||||
use crate::other::interpolated_string_element::InterpolatedElementContext;
|
use crate::other::interpolated_string::InterpolatedStringContext;
|
||||||
|
|
||||||
pub struct PyFormatContext<'a> {
|
pub struct PyFormatContext<'a> {
|
||||||
options: PyFormatOptions,
|
options: PyFormatOptions,
|
||||||
|
@ -143,7 +143,7 @@ pub(crate) enum InterpolatedStringState {
|
||||||
/// curly brace in `f"foo {x}"`.
|
/// curly brace in `f"foo {x}"`.
|
||||||
///
|
///
|
||||||
/// The containing `FStringContext` is the surrounding f-string context.
|
/// The containing `FStringContext` is the surrounding f-string context.
|
||||||
InsideInterpolatedElement(InterpolatedElementContext),
|
InsideInterpolatedElement(InterpolatedStringContext),
|
||||||
/// The formatter is outside an f-string.
|
/// The formatter is outside an f-string.
|
||||||
#[default]
|
#[default]
|
||||||
Outside,
|
Outside,
|
||||||
|
@ -153,7 +153,7 @@ impl InterpolatedStringState {
|
||||||
pub(crate) fn can_contain_line_breaks(self) -> Option<bool> {
|
pub(crate) fn can_contain_line_breaks(self) -> Option<bool> {
|
||||||
match self {
|
match self {
|
||||||
InterpolatedStringState::InsideInterpolatedElement(context) => {
|
InterpolatedStringState::InsideInterpolatedElement(context) => {
|
||||||
Some(context.can_contain_line_breaks())
|
Some(context.is_multiline())
|
||||||
}
|
}
|
||||||
InterpolatedStringState::Outside => None,
|
InterpolatedStringState::Outside => None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@ impl InterpolatedStringContext {
|
||||||
self.enclosing_flags
|
self.enclosing_flags
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const fn layout(self) -> InterpolatedStringLayout {
|
pub(crate) const fn is_multiline(self) -> bool {
|
||||||
self.layout
|
matches!(self.layout, InterpolatedStringLayout::Multiline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
||||||
use ruff_formatter::{Buffer, FormatOptions as _, RemoveSoftLinesBuffer, format_args, write};
|
use ruff_formatter::{Buffer, FormatOptions as _, RemoveSoftLinesBuffer, format_args, write};
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
AnyStringFlags, ConversionFlag, Expr, InterpolatedElement, InterpolatedStringElement,
|
AnyStringFlags, ConversionFlag, Expr, InterpolatedElement, InterpolatedStringElement,
|
||||||
InterpolatedStringLiteralElement, StringFlags,
|
InterpolatedStringLiteralElement,
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextSlice};
|
use ruff_text_size::{Ranged, TextSlice};
|
||||||
|
|
||||||
|
@ -78,52 +78,10 @@ impl Format<PyFormatContext<'_>> for FormatFStringLiteralElement<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Context representing an f-string expression element.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub(crate) struct InterpolatedElementContext {
|
|
||||||
/// The context of the parent f-string containing this expression element.
|
|
||||||
parent_context: InterpolatedStringContext,
|
|
||||||
/// Indicates whether this expression element has format specifier or not.
|
|
||||||
has_format_spec: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InterpolatedElementContext {
|
|
||||||
/// Returns the [`InterpolatedStringContext`] containing this expression element.
|
|
||||||
pub(crate) fn interpolated_string(self) -> InterpolatedStringContext {
|
|
||||||
self.parent_context
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the expression element can contain line breaks.
|
|
||||||
pub(crate) fn can_contain_line_breaks(self) -> bool {
|
|
||||||
self.parent_context.layout().is_multiline()
|
|
||||||
// For a triple-quoted f-string, the element can't be formatted into multiline if it
|
|
||||||
// has a format specifier because otherwise the newline would be treated as part of the
|
|
||||||
// format specifier.
|
|
||||||
//
|
|
||||||
// Given the following f-string:
|
|
||||||
// ```python
|
|
||||||
// f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f} ddddddddddddddd eeeeeeee"""
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// We can't format it as:
|
|
||||||
// ```python
|
|
||||||
// f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
|
||||||
// variable:.3f
|
|
||||||
// } ddddddddddddddd eeeeeeee"""
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// Here, the format specifier string would become ".3f\n", which is not what we want.
|
|
||||||
// But, if the original source code already contained a newline, they'll be preserved.
|
|
||||||
//
|
|
||||||
// The Python version is irrelevant in this case.
|
|
||||||
&& !(self.parent_context.flags().is_triple_quoted() && self.has_format_spec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Formats an f-string expression element.
|
/// Formats an f-string expression element.
|
||||||
pub(crate) struct FormatInterpolatedElement<'a> {
|
pub(crate) struct FormatInterpolatedElement<'a> {
|
||||||
element: &'a InterpolatedElement,
|
element: &'a InterpolatedElement,
|
||||||
context: InterpolatedElementContext,
|
context: InterpolatedStringContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FormatInterpolatedElement<'a> {
|
impl<'a> FormatInterpolatedElement<'a> {
|
||||||
|
@ -131,13 +89,7 @@ impl<'a> FormatInterpolatedElement<'a> {
|
||||||
element: &'a InterpolatedElement,
|
element: &'a InterpolatedElement,
|
||||||
context: InterpolatedStringContext,
|
context: InterpolatedStringContext,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self { element, context }
|
||||||
element,
|
|
||||||
context: InterpolatedElementContext {
|
|
||||||
parent_context: context,
|
|
||||||
has_format_spec: element.format_spec.is_some(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +103,8 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
..
|
..
|
||||||
} = self.element;
|
} = self.element;
|
||||||
|
|
||||||
|
let expression = &**expression;
|
||||||
|
|
||||||
if let Some(debug_text) = debug_text {
|
if let Some(debug_text) = debug_text {
|
||||||
token("{").fmt(f)?;
|
token("{").fmt(f)?;
|
||||||
|
|
||||||
|
@ -179,7 +133,7 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
NormalizedDebugText(&debug_text.leading),
|
NormalizedDebugText(&debug_text.leading),
|
||||||
verbatim_text(&**expression),
|
verbatim_text(expression),
|
||||||
NormalizedDebugText(&debug_text.trailing),
|
NormalizedDebugText(&debug_text.trailing),
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
@ -202,6 +156,8 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
let comments = f.context().comments().clone();
|
let comments = f.context().comments().clone();
|
||||||
let dangling_item_comments = comments.dangling(self.element);
|
let dangling_item_comments = comments.dangling(self.element);
|
||||||
|
|
||||||
|
let multiline = self.context.is_multiline();
|
||||||
|
|
||||||
// If an expression starts with a `{`, we need to add a space before the
|
// If an expression starts with a `{`, we need to add a space before the
|
||||||
// curly brace to avoid turning it into a literal curly with `{{`.
|
// curly brace to avoid turning it into a literal curly with `{{`.
|
||||||
//
|
//
|
||||||
|
@ -216,7 +172,7 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
// added to maintain consistency.
|
// added to maintain consistency.
|
||||||
let bracket_spacing =
|
let bracket_spacing =
|
||||||
needs_bracket_spacing(expression, f.context()).then_some(format_with(|f| {
|
needs_bracket_spacing(expression, f.context()).then_some(format_with(|f| {
|
||||||
if self.context.can_contain_line_breaks() {
|
if multiline {
|
||||||
soft_line_break_or_space().fmt(f)
|
soft_line_break_or_space().fmt(f)
|
||||||
} else {
|
} else {
|
||||||
space().fmt(f)
|
space().fmt(f)
|
||||||
|
@ -241,14 +197,21 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(format_spec) = format_spec.as_deref() {
|
if let Some(format_spec) = format_spec.as_deref() {
|
||||||
|
// ```py
|
||||||
|
// f"{
|
||||||
|
// foo
|
||||||
|
// # comment 27
|
||||||
|
// :test}"
|
||||||
|
// ```
|
||||||
|
if comments.has_trailing_own_line(expression) {
|
||||||
|
soft_line_break().fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
token(":").fmt(f)?;
|
token(":").fmt(f)?;
|
||||||
|
|
||||||
for element in &format_spec.elements {
|
for element in &format_spec.elements {
|
||||||
FormatInterpolatedStringElement::new(
|
FormatInterpolatedStringElement::new(element, self.context).fmt(f)?;
|
||||||
element,
|
}
|
||||||
self.context.interpolated_string(),
|
|
||||||
)
|
|
||||||
.fmt(f)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// These trailing comments can only occur if the format specifier is
|
// These trailing comments can only occur if the format specifier is
|
||||||
|
@ -260,10 +223,21 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
// # comment
|
// # comment
|
||||||
// }"
|
// }"
|
||||||
// ```
|
// ```
|
||||||
//
|
|
||||||
|
// This can also be triggered outside of a format spec, at
|
||||||
|
// least until https://github.com/astral-sh/ruff/issues/18632 is a syntax error
|
||||||
|
// TODO(https://github.com/astral-sh/ruff/issues/18632) Remove this
|
||||||
|
// and double check if it is still necessary for the triple quoted case
|
||||||
|
// once this is a syntax error.
|
||||||
|
// ```py
|
||||||
|
// f"{
|
||||||
|
// foo
|
||||||
|
// :{x}
|
||||||
|
// # comment 28
|
||||||
|
// } woah {x}"
|
||||||
|
// ```
|
||||||
// Any other trailing comments are attached to the expression itself.
|
// Any other trailing comments are attached to the expression itself.
|
||||||
trailing_comments(comments.trailing(self.element)).fmt(f)?;
|
trailing_comments(comments.trailing(self.element)).fmt(f)?;
|
||||||
}
|
|
||||||
|
|
||||||
if conversion.is_none() && format_spec.is_none() {
|
if conversion.is_none() && format_spec.is_none() {
|
||||||
bracket_spacing.fmt(f)?;
|
bracket_spacing.fmt(f)?;
|
||||||
|
@ -283,12 +257,31 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
|
||||||
{
|
{
|
||||||
let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);
|
let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);
|
||||||
|
|
||||||
if self.context.can_contain_line_breaks() {
|
if self.context.is_multiline() {
|
||||||
|
// TODO: The `or comments.has_trailing...` can be removed once newlines in format specs are a syntax error.
|
||||||
|
// This is to support the following case:
|
||||||
|
// ```py
|
||||||
|
// x = f"{x !s
|
||||||
|
// :>0
|
||||||
|
// # comment 21
|
||||||
|
// }"
|
||||||
|
// ```
|
||||||
|
if format_spec.is_none() || comments.has_trailing_own_line(self.element) {
|
||||||
group(&format_args![
|
group(&format_args![
|
||||||
open_parenthesis_comments,
|
open_parenthesis_comments,
|
||||||
soft_block_indent(&item)
|
soft_block_indent(&item)
|
||||||
])
|
])
|
||||||
.fmt(&mut f)?;
|
.fmt(&mut f)?;
|
||||||
|
} else {
|
||||||
|
// For strings ending with a format spec, don't add a newline between the end of the format spec
|
||||||
|
// and closing curly brace because that is invalid syntax for single quoted strings and
|
||||||
|
// the newline is preserved as part of the format spec for triple quoted strings.
|
||||||
|
group(&format_args![
|
||||||
|
open_parenthesis_comments,
|
||||||
|
indent(&format_args![soft_line_break(), item])
|
||||||
|
])
|
||||||
|
.fmt(&mut f)?;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut buffer = RemoveSoftLinesBuffer::new(&mut *f);
|
let mut buffer = RemoveSoftLinesBuffer::new(&mut *f);
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl<'a, 'src> StringNormalizer<'a, 'src> {
|
||||||
if let InterpolatedStringState::InsideInterpolatedElement(parent_context) =
|
if let InterpolatedStringState::InsideInterpolatedElement(parent_context) =
|
||||||
self.context.interpolated_string_state()
|
self.context.interpolated_string_state()
|
||||||
{
|
{
|
||||||
let parent_flags = parent_context.interpolated_string().flags();
|
let parent_flags = parent_context.flags();
|
||||||
if !parent_flags.is_triple_quoted() || string.flags().is_triple_quoted() {
|
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
|
// 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
|
// we might end up joining two f-strings that have different quote styles, in which case we need to alternate the quotes
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
## Input
|
## Input
|
||||||
|
|
||||||
|
@ -170,7 +169,7 @@ rf"\{"a"}"
|
||||||
|
|
||||||
x = """foo {{ {2 + 2}bar
|
x = """foo {{ {2 + 2}bar
|
||||||
baz"""
|
baz"""
|
||||||
@@ -28,74 +26,62 @@
|
@@ -28,55 +26,48 @@
|
||||||
|
|
||||||
x = f"""foo {{ {2 + 2}bar {{ baz"""
|
x = f"""foo {{ {2 + 2}bar {{ baz"""
|
||||||
|
|
||||||
|
@ -242,12 +241,7 @@ rf"\{"a"}"
|
||||||
|
|
||||||
f"{2+2=}"
|
f"{2+2=}"
|
||||||
f"{2+2 = }"
|
f"{2+2 = }"
|
||||||
f"{ 2 + 2 = }"
|
@@ -88,14 +79,10 @@
|
||||||
|
|
||||||
-f"""foo {
|
|
||||||
- datetime.datetime.now():%Y
|
|
||||||
+f"""foo {datetime.datetime.now():%Y
|
|
||||||
%m
|
|
||||||
%d
|
%d
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
|
@ -264,7 +258,7 @@ rf"\{"a"}"
|
||||||
)
|
)
|
||||||
|
|
||||||
f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \
|
f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \
|
||||||
@@ -105,8 +91,10 @@
|
@@ -105,8 +92,10 @@
|
||||||
rf"\{{\}}"
|
rf"\{{\}}"
|
||||||
|
|
||||||
f"""
|
f"""
|
||||||
|
@ -277,7 +271,7 @@ rf"\{"a"}"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
value: str = f"""foo
|
value: str = f"""foo
|
||||||
@@ -124,13 +112,15 @@
|
@@ -124,13 +113,15 @@
|
||||||
|
|
||||||
f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}'
|
f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}'
|
||||||
|
|
||||||
|
@ -378,7 +372,8 @@ f"{2+2=}"
|
||||||
f"{2+2 = }"
|
f"{2+2 = }"
|
||||||
f"{ 2 + 2 = }"
|
f"{ 2 + 2 = }"
|
||||||
|
|
||||||
f"""foo {datetime.datetime.now():%Y
|
f"""foo {
|
||||||
|
datetime.datetime.now():%Y
|
||||||
%m
|
%m
|
||||||
%d
|
%d
|
||||||
}"""
|
}"""
|
||||||
|
|
|
@ -248,18 +248,22 @@ f"{ # comment 15
|
||||||
}" # comment 19
|
}" # comment 19
|
||||||
# comment 20
|
# comment 20
|
||||||
|
|
||||||
# Single-quoted f-strings with a format specificer can be multiline
|
# The specifier of an f-string must hug the closing `}` because a multiline format specifier is invalid syntax in a single
|
||||||
f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
# quoted f-string.
|
||||||
variable:.3f} ddddddddddddddd eeeeeeee"
|
f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable
|
||||||
|
:.3f} ddddddddddddddd eeeeeeee"
|
||||||
|
|
||||||
# But, if it's triple-quoted then we can't or the format specificer will have a
|
# The same applies for triple quoted f-strings, except that we need to preserve the newline before the closing `}`.
|
||||||
# trailing newline
|
# or we risk altering the meaning of the f-string.
|
||||||
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable
|
||||||
variable:.3f} ddddddddddddddd eeeeeeee"""
|
:.3f} ddddddddddddddd eeeeeeee"""
|
||||||
|
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f
|
||||||
|
} ddddddddddddddd eeeeeeee"""
|
||||||
|
|
||||||
# But, we can break the ones which don't have a format specifier
|
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
f"""fooooooooooooooooooo barrrrrrrrrrrrrrrrrrr {
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd
|
||||||
xxxxxxxxxxxxxxx:.3f} aaaaaaaaaaaaaaaaa { xxxxxxxxxxxxxxxxxxxx } bbbbbbbbbbbb"""
|
# comment
|
||||||
|
:.3f} cccccccccc"""
|
||||||
|
|
||||||
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
||||||
# which is part of the format specifier
|
# which is part of the format specifier
|
||||||
|
@ -291,6 +295,13 @@ x = f"{x !s
|
||||||
# comment 21
|
# comment 21
|
||||||
}"
|
}"
|
||||||
|
|
||||||
|
x = f"{
|
||||||
|
x!s:>{
|
||||||
|
0
|
||||||
|
# comment 21-2
|
||||||
|
}}"
|
||||||
|
|
||||||
|
|
||||||
x = f"""
|
x = f"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
@ -315,6 +326,21 @@ f"{ # comment 26
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"
|
||||||
|
|
||||||
|
|
||||||
|
f"""{foo
|
||||||
|
:a{
|
||||||
|
a # comment 29
|
||||||
|
# comment 30
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/18672
|
||||||
|
f"{
|
||||||
|
# comment 31
|
||||||
|
foo
|
||||||
|
:>
|
||||||
|
}"
|
||||||
|
|
||||||
# Assignment statement
|
# Assignment statement
|
||||||
|
|
||||||
# Even though this f-string has multiline expression, thus allowing us to break it at the
|
# Even though this f-string has multiline expression, thus allowing us to break it at the
|
||||||
|
@ -1008,26 +1034,32 @@ f"{ # comment 15
|
||||||
}" # comment 19
|
}" # comment 19
|
||||||
# comment 20
|
# comment 20
|
||||||
|
|
||||||
# Single-quoted f-strings with a format specificer can be multiline
|
# The specifier of an f-string must hug the closing `}` because a multiline format specifier is invalid syntax in a single
|
||||||
|
# quoted f-string.
|
||||||
f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
|
variable:.3f} ddddddddddddddd eeeeeeee"
|
||||||
|
|
||||||
|
# The same applies for triple quoted f-strings, except that we need to preserve the newline before the closing `}`.
|
||||||
|
# or we risk altering the meaning of the f-string.
|
||||||
|
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
|
variable:.3f} ddddddddddddddd eeeeeeee"""
|
||||||
|
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
variable:.3f
|
variable:.3f
|
||||||
} ddddddddddddddd eeeeeeee"
|
} ddddddddddddddd eeeeeeee"""
|
||||||
|
|
||||||
# But, if it's triple-quoted then we can't or the format specificer will have a
|
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
# trailing newline
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd
|
||||||
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f} ddddddddddddddd eeeeeeee"""
|
# comment
|
||||||
|
:.3f} cccccccccc"""
|
||||||
# But, we can break the ones which don't have a format specifier
|
|
||||||
f"""fooooooooooooooooooo barrrrrrrrrrrrrrrrrrr {xxxxxxxxxxxxxxx:.3f} aaaaaaaaaaaaaaaaa {
|
|
||||||
xxxxxxxxxxxxxxxxxxxx
|
|
||||||
} bbbbbbbbbbbb"""
|
|
||||||
|
|
||||||
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
||||||
# which is part of the format specifier
|
# which is part of the format specifier
|
||||||
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
||||||
# comment
|
# comment
|
||||||
} cccccccccc"""
|
} cccccccccc"""
|
||||||
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
||||||
# comment} cccccccccc"""
|
# comment} cccccccccc"""
|
||||||
|
|
||||||
# Conversion flags
|
# Conversion flags
|
||||||
|
@ -1047,6 +1079,13 @@ x = f"{
|
||||||
# comment 21
|
# comment 21
|
||||||
}"
|
}"
|
||||||
|
|
||||||
|
x = f"{
|
||||||
|
x!s:>{
|
||||||
|
0
|
||||||
|
# comment 21-2
|
||||||
|
}}"
|
||||||
|
|
||||||
|
|
||||||
x = f"""
|
x = f"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
@ -1071,6 +1110,19 @@ f"{ # comment 26
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"
|
||||||
|
|
||||||
|
|
||||||
|
f"""{
|
||||||
|
foo:a{
|
||||||
|
a # comment 29
|
||||||
|
# comment 30
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/18672
|
||||||
|
f"{
|
||||||
|
# comment 31
|
||||||
|
foo:>}"
|
||||||
|
|
||||||
# Assignment statement
|
# Assignment statement
|
||||||
|
|
||||||
# Even though this f-string has multiline expression, thus allowing us to break it at the
|
# Even though this f-string has multiline expression, thus allowing us to break it at the
|
||||||
|
@ -1236,10 +1288,12 @@ aaaaaaaaaaaaaaaaaa = (
|
||||||
)
|
)
|
||||||
|
|
||||||
# The newline is only considered when it's a tripled-quoted f-string.
|
# The newline is only considered when it's a tripled-quoted f-string.
|
||||||
aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f
|
aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
|
a:.3f
|
||||||
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f
|
aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
|
a:.3f
|
||||||
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
||||||
|
|
||||||
# Remove the parentheses here
|
# Remove the parentheses here
|
||||||
|
@ -1804,26 +1858,32 @@ f"{ # comment 15
|
||||||
}" # comment 19
|
}" # comment 19
|
||||||
# comment 20
|
# comment 20
|
||||||
|
|
||||||
# Single-quoted f-strings with a format specificer can be multiline
|
# The specifier of an f-string must hug the closing `}` because a multiline format specifier is invalid syntax in a single
|
||||||
|
# quoted f-string.
|
||||||
f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
|
variable:.3f} ddddddddddddddd eeeeeeee"
|
||||||
|
|
||||||
|
# The same applies for triple quoted f-strings, except that we need to preserve the newline before the closing `}`.
|
||||||
|
# or we risk altering the meaning of the f-string.
|
||||||
|
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
|
variable:.3f} ddddddddddddddd eeeeeeee"""
|
||||||
|
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
variable:.3f
|
variable:.3f
|
||||||
} ddddddddddddddd eeeeeeee"
|
} ddddddddddddddd eeeeeeee"""
|
||||||
|
|
||||||
# But, if it's triple-quoted then we can't or the format specificer will have a
|
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
# trailing newline
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd
|
||||||
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f} ddddddddddddddd eeeeeeee"""
|
# comment
|
||||||
|
:.3f} cccccccccc"""
|
||||||
# But, we can break the ones which don't have a format specifier
|
|
||||||
f"""fooooooooooooooooooo barrrrrrrrrrrrrrrrrrr {xxxxxxxxxxxxxxx:.3f} aaaaaaaaaaaaaaaaa {
|
|
||||||
xxxxxxxxxxxxxxxxxxxx
|
|
||||||
} bbbbbbbbbbbb"""
|
|
||||||
|
|
||||||
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
||||||
# which is part of the format specifier
|
# which is part of the format specifier
|
||||||
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
||||||
# comment
|
# comment
|
||||||
} cccccccccc"""
|
} cccccccccc"""
|
||||||
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
|
||||||
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
||||||
# comment} cccccccccc"""
|
# comment} cccccccccc"""
|
||||||
|
|
||||||
# Conversion flags
|
# Conversion flags
|
||||||
|
@ -1843,6 +1903,13 @@ x = f"{
|
||||||
# comment 21
|
# comment 21
|
||||||
}"
|
}"
|
||||||
|
|
||||||
|
x = f"{
|
||||||
|
x!s:>{
|
||||||
|
0
|
||||||
|
# comment 21-2
|
||||||
|
}}"
|
||||||
|
|
||||||
|
|
||||||
x = f"""
|
x = f"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
@ -1867,6 +1934,19 @@ f"{ # comment 26
|
||||||
# comment 28
|
# comment 28
|
||||||
} woah {x}"
|
} woah {x}"
|
||||||
|
|
||||||
|
|
||||||
|
f"""{
|
||||||
|
foo:a{
|
||||||
|
a # comment 29
|
||||||
|
# comment 30
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
# Regression test for https://github.com/astral-sh/ruff/issues/18672
|
||||||
|
f"{
|
||||||
|
# comment 31
|
||||||
|
foo:>}"
|
||||||
|
|
||||||
# Assignment statement
|
# Assignment statement
|
||||||
|
|
||||||
# Even though this f-string has multiline expression, thus allowing us to break it at the
|
# Even though this f-string has multiline expression, thus allowing us to break it at the
|
||||||
|
@ -2032,10 +2112,12 @@ aaaaaaaaaaaaaaaaaa = (
|
||||||
)
|
)
|
||||||
|
|
||||||
# The newline is only considered when it's a tripled-quoted f-string.
|
# The newline is only considered when it's a tripled-quoted f-string.
|
||||||
aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f
|
aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
|
a:.3f
|
||||||
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f
|
aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
|
a:.3f
|
||||||
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
||||||
|
|
||||||
# Remove the parentheses here
|
# Remove the parentheses here
|
||||||
|
|
|
@ -246,18 +246,20 @@ t"{ # comment 15
|
||||||
}" # comment 19
|
}" # comment 19
|
||||||
# comment 20
|
# comment 20
|
||||||
|
|
||||||
# Single-quoted t-strings with a format specificer can be multiline
|
# The specifier of a t-string must hug the closing `}` because a multiline format specifier is invalid syntax in a single
|
||||||
|
# quoted f-string.
|
||||||
t"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
t"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
variable:.3f} ddddddddddddddd eeeeeeee"
|
variable
|
||||||
|
:.3f} ddddddddddddddd eeeeeeee"
|
||||||
|
|
||||||
# But, if it's triple-quoted then we can't or the format specificer will have a
|
# The same applies for triple quoted t-strings, except that we need to preserve the newline before the closing `}`.
|
||||||
# trailing newline
|
# or we risk altering the meaning of the f-string.
|
||||||
t"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
t"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable
|
||||||
variable:.3f} ddddddddddddddd eeeeeeee"""
|
:.3f} ddddddddddddddd eeeeeeee"""
|
||||||
|
t"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable
|
||||||
|
:.3f
|
||||||
|
} ddddddddddddddd eeeeeeee"""
|
||||||
|
|
||||||
# But, we can break the ones which don't have a format specifier
|
|
||||||
t"""fooooooooooooooooooo barrrrrrrrrrrrrrrrrrr {
|
|
||||||
xxxxxxxxxxxxxxx:.3f} aaaaaaaaaaaaaaaaa { xxxxxxxxxxxxxxxxxxxx } bbbbbbbbbbbb"""
|
|
||||||
|
|
||||||
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
||||||
# which is part of the format specifier
|
# which is part of the format specifier
|
||||||
|
@ -289,6 +291,12 @@ x = t"{x !s
|
||||||
# comment 21
|
# comment 21
|
||||||
}"
|
}"
|
||||||
|
|
||||||
|
x = f"{
|
||||||
|
x!s:>{
|
||||||
|
0
|
||||||
|
# comment 21-2
|
||||||
|
}}"
|
||||||
|
|
||||||
x = t"""
|
x = t"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
@ -1004,26 +1012,28 @@ t"{ # comment 15
|
||||||
}" # comment 19
|
}" # comment 19
|
||||||
# comment 20
|
# comment 20
|
||||||
|
|
||||||
# Single-quoted t-strings with a format specificer can be multiline
|
# The specifier of a t-string must hug the closing `}` because a multiline format specifier is invalid syntax in a single
|
||||||
|
# quoted f-string.
|
||||||
t"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
t"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
|
variable:.3f} ddddddddddddddd eeeeeeee"
|
||||||
|
|
||||||
|
# The same applies for triple quoted t-strings, except that we need to preserve the newline before the closing `}`.
|
||||||
|
# or we risk altering the meaning of the f-string.
|
||||||
|
t"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
|
variable:.3f} ddddddddddddddd eeeeeeee"""
|
||||||
|
t"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
|
||||||
variable:.3f
|
variable:.3f
|
||||||
} ddddddddddddddd eeeeeeee"
|
} ddddddddddddddd eeeeeeee"""
|
||||||
|
|
||||||
# But, if it's triple-quoted then we can't or the format specificer will have a
|
|
||||||
# trailing newline
|
|
||||||
t"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f} ddddddddddddddd eeeeeeee"""
|
|
||||||
|
|
||||||
# But, we can break the ones which don't have a format specifier
|
|
||||||
t"""fooooooooooooooooooo barrrrrrrrrrrrrrrrrrr {xxxxxxxxxxxxxxx:.3f} aaaaaaaaaaaaaaaaa {
|
|
||||||
xxxxxxxxxxxxxxxxxxxx
|
|
||||||
} bbbbbbbbbbbb"""
|
|
||||||
|
|
||||||
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
# Throw in a random comment in it but surprise, this is not a comment but just a text
|
||||||
# which is part of the format specifier
|
# which is part of the format specifier
|
||||||
aaaaaaaaaaa = t"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
aaaaaaaaaaa = t"""asaaaaaaaaaaaaaaaa {
|
||||||
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
||||||
# comment
|
# comment
|
||||||
} cccccccccc"""
|
} cccccccccc"""
|
||||||
aaaaaaaaaaa = t"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
aaaaaaaaaaa = t"""asaaaaaaaaaaaaaaaa {
|
||||||
|
aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f
|
||||||
# comment} cccccccccc"""
|
# comment} cccccccccc"""
|
||||||
|
|
||||||
# Conversion flags
|
# Conversion flags
|
||||||
|
@ -1043,6 +1053,12 @@ x = t"{
|
||||||
# comment 21
|
# comment 21
|
||||||
}"
|
}"
|
||||||
|
|
||||||
|
x = f"{
|
||||||
|
x!s:>{
|
||||||
|
0
|
||||||
|
# comment 21-2
|
||||||
|
}}"
|
||||||
|
|
||||||
x = t"""
|
x = t"""
|
||||||
{ # comment 22
|
{ # comment 22
|
||||||
x = :.0{y # comment 23
|
x = :.0{y # comment 23
|
||||||
|
@ -1232,10 +1248,12 @@ aaaaaaaaaaaaaaaaaa = (
|
||||||
)
|
)
|
||||||
|
|
||||||
# The newline is only considered when it's a tripled-quoted t-string.
|
# The newline is only considered when it's a tripled-quoted t-string.
|
||||||
aaaaaaaaaaaaaaaaaa = t"""testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f
|
aaaaaaaaaaaaaaaaaa = t"""testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
|
a:.3f
|
||||||
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
||||||
|
|
||||||
aaaaaaaaaaaaaaaaaa = t"""testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f
|
aaaaaaaaaaaaaaaaaa = t"""testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
|
a:.3f
|
||||||
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
}moreeeeeeeeeeeeeeeeeetest""" # comment
|
||||||
|
|
||||||
# Remove the parentheses here
|
# Remove the parentheses here
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use ruff_macros::RustDoc;
|
|
||||||
|
|
||||||
/// The target platform to assume when resolving types.
|
/// The target platform to assume when resolving types.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Serialize, serde::Deserialize, RustDoc),
|
derive(serde::Serialize, serde::Deserialize, ruff_macros::RustDoc),
|
||||||
serde(rename_all = "kebab-case")
|
serde(rename_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
pub enum PythonPlatform {
|
pub enum PythonPlatform {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue