diff --git a/crates/ruff_python_formatter/generate.py b/crates/ruff_python_formatter/generate.py index ff28a04331..3196e75507 100755 --- a/crates/ruff_python_formatter/generate.py +++ b/crates/ruff_python_formatter/generate.py @@ -37,7 +37,6 @@ for node_line in node_lines: # `FStringLiteralElement`, `FStringFormatSpec` and `FStringExpressionElement` are handled by the `FString` # implementation. if node in ( - "FString", "FStringLiteralElement", "FStringExpressionElement", "FStringFormatSpec", diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.options.json b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.options.json deleted file mode 100644 index 8925dd0a82..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.options.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { - "preview": "enabled" - } -] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_assignment.options.json b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_assignment.options.json deleted file mode 100644 index 8925dd0a82..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_assignment.options.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { - "preview": "enabled" - } -] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_preserve.options.json b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_preserve.options.json index b7d2d58057..17f4b10dbc 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_preserve.options.json +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_preserve.options.json @@ -1,11 +1,9 @@ [ { - "quote_style": "preserve", - "preview": "enabled" + "quote_style": "preserve" }, { "quote_style": "preserve", - "preview": "enabled", "target_version": "py312" } ] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/range_formatting/stub.options.json b/crates/ruff_python_formatter/resources/test/fixtures/ruff/range_formatting/stub.options.json index 94ab017cf8..7232519549 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/range_formatting/stub.options.json +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/range_formatting/stub.options.json @@ -1,6 +1,5 @@ [ { - "preview": "enabled", "source_type": "Stub" } ] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/assignment_split_value_first.options.json b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/assignment_split_value_first.options.json deleted file mode 100644 index 8925dd0a82..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/assignment_split_value_first.options.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { - "preview": "enabled" - } -] diff --git a/crates/ruff_python_formatter/src/expression/expr_f_string.rs b/crates/ruff_python_formatter/src/expression/expr_f_string.rs index a003a31577..cfbfb0243d 100644 --- a/crates/ruff_python_formatter/src/expression/expr_f_string.rs +++ b/crates/ruff_python_formatter/src/expression/expr_f_string.rs @@ -1,15 +1,14 @@ use ruff_python_ast::{AnyNodeRef, ExprFString, StringLike}; -use ruff_text_size::TextSlice; use crate::expression::parentheses::{ in_parentheses_only_group, NeedsParentheses, OptionalParentheses, }; -use crate::other::f_string::{FStringLayout, FormatFString}; +use crate::other::f_string::FStringLayout; use crate::prelude::*; use crate::string::implicit::{ FormatImplicitConcatenatedString, FormatImplicitConcatenatedStringFlat, }; -use crate::string::{Quoting, StringLikeExtensions}; +use crate::string::StringLikeExtensions; #[derive(Default)] pub struct FormatExprFString; @@ -23,7 +22,7 @@ impl FormatNodeRule for FormatExprFString { // [`ruff_python_ast::FStringValue::single`] constructor. let f_string = f_string_part.as_f_string().unwrap(); - FormatFString::new(f_string, f_string_quoting(item, f.context().source())).fmt(f) + f_string.format().fmt(f) } else { // Always join fstrings that aren't parenthesized and thus, are always on a single line. if !f.context().node_level().is_parenthesized() { @@ -58,28 +57,3 @@ impl NeedsParentheses for ExprFString { } } } - -pub(crate) fn f_string_quoting(f_string: &ExprFString, source: &str) -> Quoting { - let unprefixed = source - .slice(f_string) - .trim_start_matches(|c| c != '"' && c != '\''); - let triple_quoted = unprefixed.starts_with(r#"""""#) || unprefixed.starts_with(r"'''"); - - if f_string - .value - .elements() - .filter_map(|element| element.as_expression()) - .any(|expression| { - let string_content = source.slice(expression); - if triple_quoted { - string_content.contains(r#"""""#) || string_content.contains("'''") - } else { - string_content.contains(['"', '\'']) - } - }) - { - Quoting::Preserve - } else { - Quoting::CanChange - } -} diff --git a/crates/ruff_python_formatter/src/expression/expr_subscript.rs b/crates/ruff_python_formatter/src/expression/expr_subscript.rs index 481cb1f586..3aa6661b21 100644 --- a/crates/ruff_python_formatter/src/expression/expr_subscript.rs +++ b/crates/ruff_python_formatter/src/expression/expr_subscript.rs @@ -8,7 +8,6 @@ use crate::expression::parentheses::{ }; use crate::expression::CallChainLayout; use crate::prelude::*; -use crate::preview::is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled; #[derive(Default)] pub struct FormatExprSubscript { @@ -108,13 +107,14 @@ impl NeedsParentheses for ExprSubscript { if function.returns.as_deref().is_some_and(|returns| { AnyNodeRef::ptr_eq(returns.into(), self.into()) }) { - if is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled(context) && - function.parameters.is_empty() && !context.comments().has(&*function.parameters) { + if function.parameters.is_empty() + && !context.comments().has(&*function.parameters) + { // Apply the `optional_parentheses` layout when the subscript // is in a return type position of a function without parameters. // This ensures the subscript is parenthesized if it has a very // long name that goes over the line length limit. - return OptionalParentheses::Multiline + return OptionalParentheses::Multiline; } // Don't use the best fitting layout for return type annotation because it results in the diff --git a/crates/ruff_python_formatter/src/expression/mod.rs b/crates/ruff_python_formatter/src/expression/mod.rs index d908ee2ae1..574aee804f 100644 --- a/crates/ruff_python_formatter/src/expression/mod.rs +++ b/crates/ruff_python_formatter/src/expression/mod.rs @@ -19,10 +19,7 @@ use crate::expression::parentheses::{ OptionalParentheses, Parentheses, Parenthesize, }; use crate::prelude::*; -use crate::preview::{ - is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled, - is_f_string_formatting_enabled, is_hug_parens_with_braces_and_square_brackets_enabled, -}; +use crate::preview::is_hug_parens_with_braces_and_square_brackets_enabled; mod binary_like; pub(crate) mod expr_attribute; @@ -388,18 +385,12 @@ impl Format> for MaybeParenthesizeExpression<'_> { // is parenthesized. Unless, it's the `Parenthesize::IfBreaksParenthesizedNested` layout // where parenthesizing nested `maybe_parenthesized_expression` is explicitly desired. _ if f.context().node_level().is_parenthesized() => { - if !is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled( - f.context(), - ) { - OptionalParentheses::Never - } else if matches!(parenthesize, Parenthesize::IfBreaksParenthesizedNested) { - return parenthesize_if_expands( - &expression.format().with_options(Parentheses::Never), - ) - .with_indent(!is_expression_huggable(expression, f.context())) - .fmt(f); + return if matches!(parenthesize, Parenthesize::IfBreaksParenthesizedNested) { + parenthesize_if_expands(&expression.format().with_options(Parentheses::Never)) + .with_indent(!is_expression_huggable(expression, f.context())) + .fmt(f) } else { - return expression.format().with_options(Parentheses::Never).fmt(f); + expression.format().with_options(Parentheses::Never).fmt(f) } } needs_parentheses => needs_parentheses, @@ -409,13 +400,12 @@ impl Format> for MaybeParenthesizeExpression<'_> { match needs_parentheses { OptionalParentheses::Multiline => match parenthesize { - Parenthesize::IfBreaksParenthesized | Parenthesize::IfBreaksParenthesizedNested if !is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled(f.context()) => { - parenthesize_if_expands(&unparenthesized).fmt(f) - } - Parenthesize::IfRequired => unparenthesized.fmt(f), - Parenthesize::Optional | Parenthesize::IfBreaks | Parenthesize::IfBreaksParenthesized | Parenthesize::IfBreaksParenthesizedNested => { + Parenthesize::Optional + | Parenthesize::IfBreaks + | Parenthesize::IfBreaksParenthesized + | Parenthesize::IfBreaksParenthesizedNested => { if can_omit_optional_parentheses(expression, f.context()) { optional_parentheses(&unparenthesized).fmt(f) } else { @@ -424,9 +414,6 @@ impl Format> for MaybeParenthesizeExpression<'_> { } }, OptionalParentheses::BestFit => match parenthesize { - Parenthesize::IfBreaksParenthesized | Parenthesize::IfBreaksParenthesizedNested if !is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled(f.context()) => - parenthesize_if_expands(&unparenthesized).fmt(f), - Parenthesize::IfBreaksParenthesized | Parenthesize::IfBreaksParenthesizedNested => { // Can-omit layout is relevant for `"abcd".call`. We don't want to add unnecessary // parentheses in this case. @@ -454,15 +441,11 @@ impl Format> for MaybeParenthesizeExpression<'_> { } }, OptionalParentheses::Never => match parenthesize { - Parenthesize::IfBreaksParenthesized | Parenthesize::IfBreaksParenthesizedNested if !is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled(f.context()) => { - parenthesize_if_expands(&unparenthesized) - .with_indent(!is_expression_huggable(expression, f.context())) - .fmt(f) - } - - Parenthesize::Optional | Parenthesize::IfBreaks | Parenthesize::IfRequired | Parenthesize::IfBreaksParenthesized | Parenthesize::IfBreaksParenthesizedNested => { - unparenthesized.fmt(f) - } + Parenthesize::Optional + | Parenthesize::IfBreaks + | Parenthesize::IfRequired + | Parenthesize::IfBreaksParenthesized + | Parenthesize::IfBreaksParenthesizedNested => unparenthesized.fmt(f), }, OptionalParentheses::Always => { @@ -766,32 +749,6 @@ impl<'input> CanOmitOptionalParenthesesVisitor<'input> { return; } - Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) - if value.is_implicit_concatenated() => - { - if !is_f_string_formatting_enabled(self.context) { - self.update_max_precedence(OperatorPrecedence::String); - } - - return; - } - Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) - if value.is_implicit_concatenated() => - { - if !is_f_string_formatting_enabled(self.context) { - self.update_max_precedence(OperatorPrecedence::String); - } - - return; - } - Expr::FString(ast::ExprFString { value, .. }) if value.is_implicit_concatenated() => { - if !is_f_string_formatting_enabled(self.context) { - self.update_max_precedence(OperatorPrecedence::String); - } - - return; - } - // Non terminal nodes that don't have a termination token. Expr::Named(_) | Expr::Generator(_) | Expr::Tuple(_) => {} @@ -1193,8 +1150,6 @@ enum OperatorPrecedence { BitwiseXor, BitwiseOr, Comparator, - // Implicit string concatenation - String, BooleanOperation, Conditional, } diff --git a/crates/ruff_python_formatter/src/generated.rs b/crates/ruff_python_formatter/src/generated.rs index 90ea7b00dd..f60202d128 100644 --- a/crates/ruff_python_formatter/src/generated.rs +++ b/crates/ruff_python_formatter/src/generated.rs @@ -2935,6 +2935,34 @@ impl<'ast> IntoFormat> for ast::TypeParamParamSpec { } } +impl FormatRule> for crate::other::f_string::FormatFString { + #[inline] + fn fmt(&self, node: &ast::FString, f: &mut PyFormatter) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl<'ast> AsFormat> for ast::FString { + type Format<'a> = FormatRefWithRule< + 'a, + ast::FString, + crate::other::f_string::FormatFString, + PyFormatContext<'ast>, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new(self, crate::other::f_string::FormatFString::default()) + } +} +impl<'ast> IntoFormat> for ast::FString { + type Format = FormatOwnedWithRule< + ast::FString, + crate::other::f_string::FormatFString, + PyFormatContext<'ast>, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new(self, crate::other::f_string::FormatFString::default()) + } +} + impl FormatRule> for crate::other::string_literal::FormatStringLiteral { diff --git a/crates/ruff_python_formatter/src/other/comprehension.rs b/crates/ruff_python_formatter/src/other/comprehension.rs index 106cf1957c..c15c2ca3e6 100644 --- a/crates/ruff_python_formatter/src/other/comprehension.rs +++ b/crates/ruff_python_formatter/src/other/comprehension.rs @@ -7,7 +7,6 @@ use crate::comments::{leading_comments, trailing_comments}; use crate::expression::expr_tuple::TupleParentheses; use crate::expression::parentheses::is_expression_parenthesized; use crate::prelude::*; -use crate::preview::is_comprehension_leading_expression_comments_same_line_enabled; #[derive(Default)] pub struct FormatComprehension; @@ -36,14 +35,12 @@ impl FormatNodeRule for FormatComprehension { // ) // ] // ``` - let will_be_parenthesized = - is_comprehension_leading_expression_comments_same_line_enabled(f.context()) - && self.preserve_parentheses - && is_expression_parenthesized( - self.expression.into(), - f.context().comments().ranges(), - f.context().source(), - ); + let will_be_parenthesized = self.preserve_parentheses + && is_expression_parenthesized( + self.expression.into(), + f.context().comments().ranges(), + f.context().source(), + ); if has_leading_comments && !will_be_parenthesized { soft_line_break_or_space().fmt(f) diff --git a/crates/ruff_python_formatter/src/other/f_string.rs b/crates/ruff_python_formatter/src/other/f_string.rs index 067c4378d1..c423f39f34 100644 --- a/crates/ruff_python_formatter/src/other/f_string.rs +++ b/crates/ruff_python_formatter/src/other/f_string.rs @@ -4,8 +4,7 @@ use ruff_source_file::LineRanges; use ruff_text_size::Ranged; use crate::prelude::*; -use crate::preview::is_f_string_formatting_enabled; -use crate::string::{Quoting, StringNormalizer, StringQuotes}; +use crate::string::{StringNormalizer, StringQuotes}; use super::f_string_element::FormatFStringElement; @@ -13,66 +12,25 @@ use super::f_string_element::FormatFStringElement; /// /// For example, this would be used to format the f-string part in `"foo" f"bar {x}"` /// or the standalone f-string in `f"foo {x} bar"`. -pub(crate) struct FormatFString<'a> { - value: &'a FString, - /// The quoting of an f-string. This is determined by the parent node - /// (f-string expression) and is required to format an f-string correctly. - quoting: Quoting, -} +#[derive(Default)] +pub struct FormatFString; -impl<'a> FormatFString<'a> { - pub(crate) fn new(value: &'a FString, quoting: Quoting) -> Self { - Self { value, quoting } - } -} +impl FormatNodeRule for FormatFString { + fn fmt_fields(&self, item: &FString, f: &mut PyFormatter) -> FormatResult<()> { + let normalizer = StringNormalizer::from_context(f.context()); -impl Format> for FormatFString<'_> { - fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> { - // If the preview style is enabled, make the decision on what quotes to use locally for each - // f-string instead of globally for the entire f-string expression. - let quoting = if is_f_string_formatting_enabled(f.context()) { - Quoting::CanChange - } else { - self.quoting - }; - - let normalizer = StringNormalizer::from_context(f.context()).with_quoting(quoting); - - // If f-string formatting is disabled (not in preview), then we will - // fall back to the previous behavior of normalizing the f-string. - if !is_f_string_formatting_enabled(f.context()) { - let result = normalizer.normalize(self.value.into()).fmt(f); - let comments = f.context().comments(); - self.value.elements.iter().for_each(|value| { - comments.mark_verbatim_node_comments_formatted(value.into()); - // Above method doesn't mark the trailing comments of the f-string elements - // as formatted, so we need to do it manually. For example, - // - // ```python - // f"""foo { - // x:.3f - // # comment - // }""" - // ``` - for trailing_comment in comments.trailing(value) { - trailing_comment.mark_formatted(); - } - }); - return result; - } - - let string_kind = normalizer.choose_quotes(self.value.into()).flags(); + let string_kind = normalizer.choose_quotes(item.into()).flags(); let context = FStringContext::new( string_kind, - FStringLayout::from_f_string(self.value, f.context().source()), + FStringLayout::from_f_string(item, f.context().source()), ); // Starting prefix and quote let quotes = StringQuotes::from(string_kind); write!(f, [string_kind.prefix(), quotes])?; - for element in &self.value.elements { + for element in &item.elements { FormatFStringElement::new(element, context).fmt(f)?; } diff --git a/crates/ruff_python_formatter/src/other/f_string_element.rs b/crates/ruff_python_formatter/src/other/f_string_element.rs index 7139b72af6..6fe59c0d12 100644 --- a/crates/ruff_python_formatter/src/other/f_string_element.rs +++ b/crates/ruff_python_formatter/src/other/f_string_element.rs @@ -61,8 +61,7 @@ impl<'a> FormatFStringLiteralElement<'a> { impl Format> for FormatFStringLiteralElement<'_> { fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> { let literal_content = f.context().source().slice(self.element); - let normalized = - normalize_string(literal_content, 0, self.fstring_flags, false, false, true); + let normalized = normalize_string(literal_content, 0, self.fstring_flags, false); match &normalized { Cow::Borrowed(_) => source_text_slice(self.element.range()).fmt(f), Cow::Owned(normalized) => text(normalized).fmt(f), diff --git a/crates/ruff_python_formatter/src/other/match_case.rs b/crates/ruff_python_formatter/src/other/match_case.rs index 3fd5fd1fea..5964af3244 100644 --- a/crates/ruff_python_formatter/src/other/match_case.rs +++ b/crates/ruff_python_formatter/src/other/match_case.rs @@ -1,15 +1,10 @@ -use ruff_formatter::{write, FormatRuleWithOptions}; -use ruff_python_ast::AstNode; +use ruff_formatter::{format_args, write, FormatRuleWithOptions}; use ruff_python_ast::MatchCase; -use crate::builders::parenthesize_if_expands; use crate::expression::maybe_parenthesize_expression; -use crate::expression::parentheses::{ - NeedsParentheses, OptionalParentheses, Parentheses, Parenthesize, -}; +use crate::expression::parentheses::Parenthesize; use crate::pattern::maybe_parenthesize_pattern; use crate::prelude::*; -use crate::preview::is_match_case_parentheses_enabled; use crate::statement::clause::{clause_body, clause_header, ClauseHeader}; use crate::statement::suite::SuiteKind; @@ -39,42 +34,12 @@ impl FormatNodeRule for FormatMatchCase { let comments = f.context().comments().clone(); let dangling_item_comments = comments.dangling(item); - let format_pattern = format_with(|f| { - if is_match_case_parentheses_enabled(f.context()) { - maybe_parenthesize_pattern(pattern, item).fmt(f) - } else { - let has_comments = - comments.has_leading(pattern) || comments.has_trailing_own_line(pattern); - - if has_comments { - pattern.format().with_options(Parentheses::Always).fmt(f) - } else { - match pattern.needs_parentheses(item.as_any_node_ref(), f.context()) { - OptionalParentheses::Multiline => parenthesize_if_expands( - &pattern.format().with_options(Parentheses::Never), - ) - .fmt(f), - OptionalParentheses::Always => { - pattern.format().with_options(Parentheses::Always).fmt(f) - } - OptionalParentheses::Never | OptionalParentheses::BestFit => { - pattern.format().with_options(Parentheses::Never).fmt(f) - } - } - } - } - }); - let format_guard = guard.as_deref().map(|guard| { format_with(|f| { write!(f, [space(), token("if"), space()])?; - if is_match_case_parentheses_enabled(f.context()) { - maybe_parenthesize_expression(guard, item, Parenthesize::IfBreaksParenthesized) - .fmt(f) - } else { - guard.format().fmt(f) - } + maybe_parenthesize_expression(guard, item, Parenthesize::IfBreaksParenthesized) + .fmt(f) }) }); @@ -84,9 +49,12 @@ impl FormatNodeRule for FormatMatchCase { clause_header( ClauseHeader::MatchCase(item), dangling_item_comments, - &format_with(|f| { - write!(f, [token("case"), space(), format_pattern, format_guard]) - }), + &format_args![ + token("case"), + space(), + maybe_parenthesize_pattern(pattern, item), + format_guard + ], ), clause_body( body, diff --git a/crates/ruff_python_formatter/src/other/string_literal.rs b/crates/ruff_python_formatter/src/other/string_literal.rs index 7aee127d4b..b1fbe18df3 100644 --- a/crates/ruff_python_formatter/src/other/string_literal.rs +++ b/crates/ruff_python_formatter/src/other/string_literal.rs @@ -2,8 +2,7 @@ use ruff_formatter::FormatRuleWithOptions; use ruff_python_ast::StringLiteral; use crate::prelude::*; -use crate::preview::is_f_string_implicit_concatenated_string_literal_quotes_enabled; -use crate::string::{docstring, Quoting, StringNormalizer}; +use crate::string::{docstring, StringNormalizer}; use crate::QuoteStyle; #[derive(Default)] @@ -28,12 +27,6 @@ pub enum StringLiteralKind { String, /// A string literal used as a docstring. Docstring, - /// A string literal that is implicitly concatenated with an f-string. This - /// makes the overall expression an f-string whose quoting detection comes - /// from the parent node (f-string expression). - #[deprecated] - #[allow(private_interfaces)] - InImplicitlyConcatenatedFString(Quoting), } impl StringLiteralKind { @@ -41,26 +34,6 @@ impl StringLiteralKind { pub(crate) const fn is_docstring(self) -> bool { matches!(self, StringLiteralKind::Docstring) } - - /// Returns the quoting to be used for this string literal. - fn quoting(self, context: &PyFormatContext) -> Quoting { - match self { - StringLiteralKind::String | StringLiteralKind::Docstring => Quoting::CanChange, - #[allow(deprecated)] - StringLiteralKind::InImplicitlyConcatenatedFString(quoting) => { - // Allow string literals to pick the "optimal" quote character - // even if any other fstring in the implicit concatenation uses an expression - // containing a quote character. - // TODO: Remove StringLiteralKind::InImplicitlyConcatenatedFString when promoting - // this style to stable and remove the layout from `AnyStringPart::String`. - if is_f_string_implicit_concatenated_string_literal_quotes_enabled(context) { - Quoting::CanChange - } else { - quoting - } - } - } - } } impl FormatNodeRule for FormatStringLiteral { @@ -75,7 +48,6 @@ impl FormatNodeRule for FormatStringLiteral { }; let normalized = StringNormalizer::from_context(f.context()) - .with_quoting(self.layout.quoting(f.context())) .with_preferred_quote_style(quote_style) .normalize(item.into()); diff --git a/crates/ruff_python_formatter/src/other/with_item.rs b/crates/ruff_python_formatter/src/other/with_item.rs index 2a0f3162f2..d6f8ed9fb4 100644 --- a/crates/ruff_python_formatter/src/other/with_item.rs +++ b/crates/ruff_python_formatter/src/other/with_item.rs @@ -6,9 +6,7 @@ use crate::expression::parentheses::{ is_expression_parenthesized, parenthesized, Parentheses, Parenthesize, }; use crate::prelude::*; -use crate::preview::{ - is_with_single_item_pre_39_enabled, is_with_single_target_parentheses_enabled, -}; +use crate::preview::is_with_single_target_parentheses_enabled; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum WithItemLayout { @@ -154,9 +152,7 @@ impl FormatNodeRule for FormatWithItem { } WithItemLayout::Python38OrOlder { single } => { - let parenthesize = if (single && is_with_single_item_pre_39_enabled(f.context())) - || is_parenthesized - { + let parenthesize = if single || is_parenthesized { Parenthesize::IfBreaks } else { Parenthesize::IfRequired diff --git a/crates/ruff_python_formatter/src/pattern/mod.rs b/crates/ruff_python_formatter/src/pattern/mod.rs index d2ba4801dd..a670a3e3c2 100644 --- a/crates/ruff_python_formatter/src/pattern/mod.rs +++ b/crates/ruff_python_formatter/src/pattern/mod.rs @@ -14,7 +14,6 @@ use crate::expression::parentheses::{ optional_parentheses, parenthesized, NeedsParentheses, OptionalParentheses, Parentheses, }; use crate::prelude::*; -use crate::preview::is_join_implicit_concatenated_string_enabled; pub(crate) mod pattern_arguments; pub(crate) mod pattern_keyword; @@ -227,7 +226,7 @@ pub(crate) fn can_pattern_omit_optional_parentheses( pattern: &Pattern, context: &PyFormatContext, ) -> bool { - let mut visitor = CanOmitOptionalParenthesesVisitor::new(context); + let mut visitor = CanOmitOptionalParenthesesVisitor::default(); visitor.visit_pattern(pattern, context); if !visitor.any_parenthesized_expressions { @@ -272,32 +271,16 @@ pub(crate) fn can_pattern_omit_optional_parentheses( } } -#[derive(Debug)] +#[derive(Debug, Default)] struct CanOmitOptionalParenthesesVisitor<'input> { max_precedence: OperatorPrecedence, max_precedence_count: usize, any_parenthesized_expressions: bool, - join_implicit_concatenated_strings: bool, last: Option<&'input Pattern>, first: First<'input>, } impl<'a> CanOmitOptionalParenthesesVisitor<'a> { - fn new(context: &PyFormatContext) -> Self { - Self { - max_precedence: OperatorPrecedence::default(), - max_precedence_count: 0, - any_parenthesized_expressions: false, - // TODO: Derive default for `CanOmitOptionalParenthesesVisitor` when removing the `join_implicit_concatenated_strings` - // preview style. - join_implicit_concatenated_strings: is_join_implicit_concatenated_string_enabled( - context, - ), - last: None, - first: First::default(), - } - } - fn visit_pattern(&mut self, pattern: &'a Pattern, context: &PyFormatContext) { match pattern { Pattern::MatchSequence(_) | Pattern::MatchMapping(_) => { @@ -305,27 +288,11 @@ impl<'a> CanOmitOptionalParenthesesVisitor<'a> { } Pattern::MatchValue(value) => match &*value.value { - Expr::StringLiteral(string) => { - if !self.join_implicit_concatenated_strings { - self.update_max_precedence(OperatorPrecedence::String, string.value.len()); - } - } - Expr::BytesLiteral(bytes) => { - if !self.join_implicit_concatenated_strings { - self.update_max_precedence(OperatorPrecedence::String, bytes.value.len()); - } - } + Expr::StringLiteral(_) | + Expr::BytesLiteral(_) | // F-strings are allowed according to python's grammar but fail with a syntax error at runtime. // That's why we need to support them for formatting. - Expr::FString(string) => { - if !self.join_implicit_concatenated_strings { - self.update_max_precedence( - OperatorPrecedence::String, - string.value.as_slice().len(), - ); - } - } - + Expr::FString(_) | Expr::NumberLiteral(_) | Expr::Attribute(_) | Expr::UnaryOp(_) => { // require no state update other than visit_pattern does. } @@ -397,8 +364,6 @@ enum OperatorPrecedence { None, Additive, Or, - // Implicit string concatenation - String, } #[derive(Copy, Clone, Debug, Default)] diff --git a/crates/ruff_python_formatter/src/pattern/pattern_match_as.rs b/crates/ruff_python_formatter/src/pattern/pattern_match_as.rs index b4db7662f5..68958cf02d 100644 --- a/crates/ruff_python_formatter/src/pattern/pattern_match_as.rs +++ b/crates/ruff_python_formatter/src/pattern/pattern_match_as.rs @@ -5,7 +5,6 @@ use ruff_python_ast::PatternMatchAs; use crate::comments::dangling_comments; use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::prelude::*; -use crate::preview::is_match_case_parentheses_enabled; #[derive(Default)] pub struct FormatPatternMatchAs; @@ -55,16 +54,12 @@ impl NeedsParentheses for PatternMatchAs { fn needs_parentheses( &self, _parent: AnyNodeRef, - context: &PyFormatContext, + _context: &PyFormatContext, ) -> OptionalParentheses { - if is_match_case_parentheses_enabled(context) { - if self.name.is_some() { - OptionalParentheses::Multiline - } else { - OptionalParentheses::BestFit - } - } else { + if self.name.is_some() { OptionalParentheses::Multiline + } else { + OptionalParentheses::BestFit } } } diff --git a/crates/ruff_python_formatter/src/pattern/pattern_match_or.rs b/crates/ruff_python_formatter/src/pattern/pattern_match_or.rs index 8364491bce..c3119a1ca5 100644 --- a/crates/ruff_python_formatter/src/pattern/pattern_match_or.rs +++ b/crates/ruff_python_formatter/src/pattern/pattern_match_or.rs @@ -8,7 +8,6 @@ use crate::expression::parentheses::{ OptionalParentheses, }; use crate::prelude::*; -use crate::preview::is_match_case_parentheses_enabled; #[derive(Default)] pub struct FormatPatternMatchOr; @@ -43,11 +42,7 @@ impl FormatNodeRule for FormatPatternMatchOr { Ok(()) }); - if is_match_case_parentheses_enabled(f.context()) { - in_parentheses_only_group(&inner).fmt(f) - } else { - inner.fmt(f) - } + in_parentheses_only_group(&inner).fmt(f) } } diff --git a/crates/ruff_python_formatter/src/pattern/pattern_match_singleton.rs b/crates/ruff_python_formatter/src/pattern/pattern_match_singleton.rs index 8e15c3357d..feb3d98919 100644 --- a/crates/ruff_python_formatter/src/pattern/pattern_match_singleton.rs +++ b/crates/ruff_python_formatter/src/pattern/pattern_match_singleton.rs @@ -3,7 +3,6 @@ use ruff_python_ast::{PatternMatchSingleton, Singleton}; use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::prelude::*; -use crate::preview::is_match_case_parentheses_enabled; #[derive(Default)] pub struct FormatPatternMatchSingleton; @@ -22,12 +21,8 @@ impl NeedsParentheses for PatternMatchSingleton { fn needs_parentheses( &self, _parent: AnyNodeRef, - context: &PyFormatContext, + _context: &PyFormatContext, ) -> OptionalParentheses { - if is_match_case_parentheses_enabled(context) { - OptionalParentheses::BestFit - } else { - OptionalParentheses::Never - } + OptionalParentheses::BestFit } } diff --git a/crates/ruff_python_formatter/src/pattern/pattern_match_value.rs b/crates/ruff_python_formatter/src/pattern/pattern_match_value.rs index 7e23c75795..5f8efadd9e 100644 --- a/crates/ruff_python_formatter/src/pattern/pattern_match_value.rs +++ b/crates/ruff_python_formatter/src/pattern/pattern_match_value.rs @@ -3,7 +3,6 @@ use ruff_python_ast::PatternMatchValue; use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses, Parentheses}; use crate::prelude::*; -use crate::preview::is_match_case_parentheses_enabled; #[derive(Default)] pub struct FormatPatternMatchValue; @@ -21,10 +20,6 @@ impl NeedsParentheses for PatternMatchValue { parent: AnyNodeRef, context: &PyFormatContext, ) -> OptionalParentheses { - if is_match_case_parentheses_enabled(context) { - self.value.needs_parentheses(parent, context) - } else { - OptionalParentheses::Never - } + self.value.needs_parentheses(parent, context) } } diff --git a/crates/ruff_python_formatter/src/preview.rs b/crates/ruff_python_formatter/src/preview.rs index c9b0c0968e..c129420ff8 100644 --- a/crates/ruff_python_formatter/src/preview.rs +++ b/crates/ruff_python_formatter/src/preview.rs @@ -14,65 +14,6 @@ pub(crate) const fn is_hug_parens_with_braces_and_square_brackets_enabled( context.is_preview() } -/// Returns `true` if the [`f-string formatting`](https://github.com/astral-sh/ruff/issues/7594) preview style is enabled. -/// WARNING: This preview style depends on `is_f_string_implicit_concatenated_string_literal_quotes_enabled`. -/// TODO: Remove `Quoting` when promoting this preview style and convert `FormatStringPart` etc. regular `FormatWithRule` implementations. -/// TODO: Remove `format_f_string` from `normalize_string` when promoting this preview style. -pub(crate) fn is_f_string_formatting_enabled(context: &PyFormatContext) -> bool { - context.is_preview() -} - -/// See [#13539](https://github.com/astral-sh/ruff/pull/13539) -/// Remove `Quoting` when stabilizing this preview style. -pub(crate) fn is_f_string_implicit_concatenated_string_literal_quotes_enabled( - context: &PyFormatContext, -) -> bool { - context.is_preview() -} - -pub(crate) fn is_with_single_item_pre_39_enabled(context: &PyFormatContext) -> bool { - context.is_preview() -} - -/// See [#12282](https://github.com/astral-sh/ruff/pull/12282). -pub(crate) fn is_comprehension_leading_expression_comments_same_line_enabled( - context: &PyFormatContext, -) -> bool { - context.is_preview() -} - -/// See [#9447](https://github.com/astral-sh/ruff/issues/9447) -pub(crate) fn is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled( - context: &PyFormatContext, -) -> bool { - context.is_preview() -} - -/// See [#6933](https://github.com/astral-sh/ruff/issues/6933). -/// This style also covers the black preview styles `remove_redundant_guard_parens` and `parens_for_long_if_clauses_in_case_block `. -/// WARNING: This preview style depends on `is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled` -/// because it relies on the new semantic of `IfBreaksParenthesized`. -pub(crate) fn is_match_case_parentheses_enabled(context: &PyFormatContext) -> bool { - context.is_preview() -} - -/// This preview style fixes a bug with the docstring's `line-length` calculation when using the `dynamic` mode. -/// The new style now respects the indent **inside** the docstring and reduces the `line-length` accordingly -/// so that the docstring's code block fits into the global line-length setting. -pub(crate) fn is_docstring_code_block_in_docstring_indent_enabled( - context: &PyFormatContext, -) -> bool { - context.is_preview() -} - -/// Returns `true` if implicitly concatenated strings should be joined if they all fit on a single line. -/// See [#9457](https://github.com/astral-sh/ruff/issues/9457) -/// WARNING: This preview style depends on `is_empty_parameters_no_unnecessary_parentheses_around_return_value_enabled` -/// because it relies on the new semantic of `IfBreaksParenthesized`. -pub(crate) fn is_join_implicit_concatenated_string_enabled(context: &PyFormatContext) -> bool { - context.is_preview() -} - /// Returns `true` if the bugfix for single-with items with a trailing comment targeting Python 3.9 or newer is enabled. /// /// See [#14001](https://github.com/astral-sh/ruff/issues/14001) diff --git a/crates/ruff_python_formatter/src/statement/stmt_assert.rs b/crates/ruff_python_formatter/src/statement/stmt_assert.rs index 2e7e99240b..099ce3c24d 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_assert.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_assert.rs @@ -3,10 +3,8 @@ use ruff_formatter::write; use ruff_python_ast::StmtAssert; use crate::comments::SourceComment; - use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; -use crate::preview::is_join_implicit_concatenated_string_enabled; use crate::{has_skip_comment, prelude::*}; #[derive(Default)] @@ -30,18 +28,12 @@ impl FormatNodeRule for FormatStmtAssert { )?; if let Some(msg) = msg { - let parenthesize = if is_join_implicit_concatenated_string_enabled(f.context()) { - Parenthesize::IfBreaksParenthesized - } else { - Parenthesize::IfBreaks - }; - write!( f, [ token(","), space(), - maybe_parenthesize_expression(msg, item, parenthesize), + maybe_parenthesize_expression(msg, item, Parenthesize::IfBreaksParenthesized), ] )?; } diff --git a/crates/ruff_python_formatter/src/statement/stmt_assign.rs b/crates/ruff_python_formatter/src/statement/stmt_assign.rs index 51e355d6cd..c92f568652 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_assign.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_assign.rs @@ -1,7 +1,7 @@ use ruff_formatter::{format_args, write, FormatError, RemoveSoftLinesBuffer}; use ruff_python_ast::{ - AnyNodeRef, Expr, ExprAttribute, ExprCall, FStringPart, Operator, StmtAssign, StringLike, - TypeParams, + AnyNodeRef, Expr, ExprAttribute, ExprCall, FString, FStringPart, Operator, StmtAssign, + StringLike, TypeParams, }; use crate::builders::parenthesize_if_expands; @@ -9,7 +9,6 @@ use crate::comments::{ trailing_comments, Comments, LeadingDanglingTrailingComments, SourceComment, }; use crate::context::{NodeLevel, WithNodeLevel}; -use crate::expression::expr_f_string::f_string_quoting; use crate::expression::parentheses::{ is_expression_parenthesized, optional_parentheses, NeedsParentheses, OptionalParentheses, Parentheses, Parenthesize, @@ -18,10 +17,7 @@ use crate::expression::{ can_omit_optional_parentheses, has_own_parentheses, has_parentheses, maybe_parenthesize_expression, }; -use crate::other::f_string::{FStringLayout, FormatFString}; -use crate::preview::{ - is_f_string_formatting_enabled, is_join_implicit_concatenated_string_enabled, -}; +use crate::other::f_string::FStringLayout; use crate::statement::trailing_semicolon; use crate::string::implicit::{ FormatImplicitConcatenatedStringExpanded, FormatImplicitConcatenatedStringFlat, @@ -456,7 +452,7 @@ impl Format> for FormatStatementsLastExpression<'_> { let f_string_flat = format_with(|f| { let mut buffer = RemoveSoftLinesBuffer::new(&mut *f); - write!(buffer, [format_f_string]) + write!(buffer, [format_f_string.format()]) }) .memoized(); @@ -518,7 +514,7 @@ impl Format> for FormatStatementsLastExpression<'_> { // }moreeeeeeeeeeeeeeeee" // ``` let format_f_string = - format_with(|f| write!(f, [format_f_string, inline_comments])); + format_with(|f| write!(f, [format_f_string.format(), inline_comments])); best_fitting![single_line, joined_parenthesized, format_f_string] .with_mode(BestFittingMode::AllLines) @@ -668,7 +664,7 @@ impl Format> for FormatStatementsLastExpression<'_> { // Similar to above, remove any soft line breaks emitted by the f-string // formatting. let mut buffer = RemoveSoftLinesBuffer::new(&mut *f); - write!(buffer, [format_f_string]) + write!(buffer, [format_f_string.format()]) } else { value.format().with_options(Parentheses::Never).fmt(f) } @@ -701,20 +697,22 @@ impl Format> for FormatStatementsLastExpression<'_> { // ) // ``` let flat_target_parenthesize_value = format_with(|f| { - write!(f, [last_target, space(), operator, space(), token("("),])?; - - if is_join_implicit_concatenated_string_enabled(f.context()) { - group(&soft_block_indent(&format_args![ - format_value, - inline_comments - ])) - .should_expand(true) - .fmt(f)?; - } else { - block_indent(&format_args![format_value, inline_comments]).fmt(f)?; - } - - token(")").fmt(f) + write!( + f, + [ + last_target, + space(), + operator, + space(), + token("("), + group(&soft_block_indent(&format_args![ + format_value, + inline_comments + ])) + .should_expand(true), + token(")") + ] + ) }); // Fall back to parenthesizing (or splitting) the last target part if we can't make the value @@ -726,15 +724,16 @@ impl Format> for FormatStatementsLastExpression<'_> { // ] = c // ``` let split_target_flat_value = format_with(|f| { - if is_join_implicit_concatenated_string_enabled(f.context()) { - group(&last_target).should_expand(true).fmt(f)?; - } else { - last_target.fmt(f)?; - } - write!( f, - [space(), operator, space(), format_value, inline_comments] + [ + group(&last_target).should_expand(true), + space(), + operator, + space(), + format_value, + inline_comments + ] ) }); @@ -935,7 +934,8 @@ impl Format> for FormatStatementsLastExpression<'_> { } let format_f_string = - format_with(|f| write!(f, [format_f_string, inline_comments])).memoized(); + format_with(|f| write!(f, [format_f_string.format(), inline_comments])) + .memoized(); // Considering the following initial source: // @@ -1102,11 +1102,7 @@ impl Format> for FormatStatementsLastExpression<'_> { fn format_f_string_assignment<'a>( string: StringLike<'a>, context: &PyFormatContext, -) -> Option> { - if !is_f_string_formatting_enabled(context) { - return None; - } - +) -> Option<&'a FString> { let StringLike::FString(expr) = string else { return None; }; @@ -1128,10 +1124,7 @@ fn format_f_string_assignment<'a>( return None; } - Some(FormatFString::new( - f_string, - f_string_quoting(expr, context.source()), - )) + Some(f_string) } #[derive(Debug, Default)] diff --git a/crates/ruff_python_formatter/src/string/docstring.rs b/crates/ruff_python_formatter/src/string/docstring.rs index 394cf09d09..5d0a923752 100644 --- a/crates/ruff_python_formatter/src/string/docstring.rs +++ b/crates/ruff_python_formatter/src/string/docstring.rs @@ -18,10 +18,6 @@ use { ruff_text_size::{Ranged, TextLen, TextRange, TextSize}, }; -use crate::preview::{ - is_docstring_code_block_in_docstring_indent_enabled, - is_join_implicit_concatenated_string_enabled, -}; use crate::string::StringQuotes; use crate::{prelude::*, DocstringCodeLineWidth, FormatModuleError}; @@ -171,7 +167,7 @@ pub(crate) fn format(normalized: &NormalizedString, f: &mut PyFormatter) -> Form if docstring[first.len()..].trim().is_empty() { // For `"""\n"""` or other whitespace between the quotes, black keeps a single whitespace, // but `""""""` doesn't get one inserted. - if needs_chaperone_space(normalized.flags(), trim_end, f.context()) + if needs_chaperone_space(normalized.flags(), trim_end) || (trim_end.is_empty() && !docstring.is_empty()) { space().fmt(f)?; @@ -211,7 +207,7 @@ pub(crate) fn format(normalized: &NormalizedString, f: &mut PyFormatter) -> Form let trim_end = docstring .as_ref() .trim_end_matches(|c: char| c.is_whitespace() && c != '\n'); - if needs_chaperone_space(normalized.flags(), trim_end, f.context()) { + if needs_chaperone_space(normalized.flags(), trim_end) { space().fmt(f)?; } @@ -508,17 +504,15 @@ impl<'src> DocstringLinePrinter<'_, '_, '_, 'src> { .to_ascii_spaces(indent_width) .saturating_add(kind.extra_indent_ascii_spaces()); - if is_docstring_code_block_in_docstring_indent_enabled(self.f.context()) { - // Add the in-docstring indentation - current_indent = current_indent.saturating_add( - u16::try_from( - kind.indent() - .columns() - .saturating_sub(self.stripped_indentation.columns()), - ) - .unwrap_or(u16::MAX), - ); - } + // Add the in-docstring indentation + current_indent = current_indent.saturating_add( + u16::try_from( + kind.indent() + .columns() + .saturating_sub(self.stripped_indentation.columns()), + ) + .unwrap_or(u16::MAX), + ); let width = std::cmp::max(1, global_line_width.saturating_sub(current_indent)); LineWidth::try_from(width).expect("width should be capped at a minimum of 1") @@ -1607,17 +1601,11 @@ fn docstring_format_source( /// If the last line of the docstring is `content" """` or `content\ """`, we need a chaperone space /// that avoids `content""""` and `content\"""`. This does only applies to un-escaped backslashes, /// so `content\\ """` doesn't need a space while `content\\\ """` does. -pub(super) fn needs_chaperone_space( - flags: AnyStringFlags, - trim_end: &str, - context: &PyFormatContext, -) -> bool { +pub(super) fn needs_chaperone_space(flags: AnyStringFlags, trim_end: &str) -> bool { if trim_end.chars().rev().take_while(|c| *c == '\\').count() % 2 == 1 { true - } else if is_join_implicit_concatenated_string_enabled(context) { - flags.is_triple_quoted() && trim_end.ends_with(flags.quote_style().as_char()) } else { - trim_end.ends_with(flags.quote_style().as_char()) + flags.is_triple_quoted() && trim_end.ends_with(flags.quote_style().as_char()) } } diff --git a/crates/ruff_python_formatter/src/string/implicit.rs b/crates/ruff_python_formatter/src/string/implicit.rs index 3db5fc7f9c..c13ec2d593 100644 --- a/crates/ruff_python_formatter/src/string/implicit.rs +++ b/crates/ruff_python_formatter/src/string/implicit.rs @@ -11,13 +11,9 @@ use std::borrow::Cow; use crate::comments::{leading_comments, trailing_comments}; use crate::expression::parentheses::in_parentheses_only_soft_line_break_or_space; -use crate::other::f_string::{FStringContext, FStringLayout, FormatFString}; +use crate::other::f_string::{FStringContext, FStringLayout}; use crate::other::f_string_element::FormatFStringExpressionElement; -use crate::other::string_literal::StringLiteralKind; use crate::prelude::*; -use crate::preview::{ - is_f_string_formatting_enabled, is_join_implicit_concatenated_string_enabled, -}; use crate::string::docstring::needs_chaperone_space; use crate::string::normalize::{ is_fstring_with_quoted_debug_expression, is_fstring_with_quoted_format_spec_and_debug, @@ -82,14 +78,9 @@ impl<'a> FormatImplicitConcatenatedStringExpanded<'a> { impl Format> for FormatImplicitConcatenatedStringExpanded<'_> { fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { let comments = f.context().comments().clone(); - let quoting = self.string.quoting(f.context().source()); - - let join_implicit_concatenated_string_enabled = - is_join_implicit_concatenated_string_enabled(f.context()); // Keep implicit concatenated strings expanded unless they're already written on a single line. if matches!(self.layout, ImplicitConcatenatedLayout::Multipart) - && join_implicit_concatenated_string_enabled && self.string.parts().tuple_windows().any(|(a, b)| { f.context() .source() @@ -103,23 +94,13 @@ impl Format> for FormatImplicitConcatenatedStringExpanded<'_ for part in self.string.parts() { let format_part = format_with(|f: &mut PyFormatter| match part { - StringLikePart::String(part) => { - let kind = if self.string.is_fstring() { - #[allow(deprecated)] - StringLiteralKind::InImplicitlyConcatenatedFString(quoting) - } else { - StringLiteralKind::String - }; - - part.format().with_options(kind).fmt(f) - } + StringLikePart::String(part) => part.format().fmt(f), StringLikePart::Bytes(bytes_literal) => bytes_literal.format().fmt(f), - StringLikePart::FString(part) => FormatFString::new(part, quoting).fmt(f), + StringLikePart::FString(part) => part.format().fmt(f), }); let part_comments = comments.leading_dangling_trailing(&part); joiner.entry(&format_args![ - (!join_implicit_concatenated_string_enabled).then_some(line_suffix_boundary()), leading_comments(part_comments.leading), format_part, trailing_comments(part_comments.trailing) @@ -149,10 +130,6 @@ impl<'a> FormatImplicitConcatenatedStringFlat<'a> { /// Creates a new formatter. Returns `None` if the string can't be merged into a single string. pub(crate) fn new(string: StringLike<'a>, context: &PyFormatContext) -> Option { fn merge_flags(string: StringLike, context: &PyFormatContext) -> Option { - if !is_join_implicit_concatenated_string_enabled(context) { - return None; - } - // Multiline strings can never fit on a single line. if string.is_multiline(context) { return None; @@ -323,44 +300,29 @@ impl Format> for FormatImplicitConcatenatedStringFlat<'_> { } StringLikePart::FString(f_string) => { - if is_f_string_formatting_enabled(f.context()) { - for element in &f_string.elements { - match element { - FStringElement::Literal(literal) => { - FormatLiteralContent { - range: literal.range(), - flags: self.flags, - is_fstring: true, - trim_end: false, - trim_start: false, - } - .fmt(f)?; + for element in &f_string.elements { + match element { + FStringElement::Literal(literal) => { + FormatLiteralContent { + range: literal.range(), + flags: self.flags, + is_fstring: true, + trim_end: false, + trim_start: false, } - // Formatting the expression here and in the expanded version is safe **only** - // because we assert that the f-string never contains any comments. - FStringElement::Expression(expression) => { - let context = FStringContext::new( - self.flags, - FStringLayout::from_f_string( - f_string, - f.context().source(), - ), - ); + .fmt(f)?; + } + // Formatting the expression here and in the expanded version is safe **only** + // because we assert that the f-string never contains any comments. + FStringElement::Expression(expression) => { + let context = FStringContext::new( + self.flags, + FStringLayout::from_f_string(f_string, f.context().source()), + ); - FormatFStringExpressionElement::new(expression, context) - .fmt(f)?; - } + FormatFStringExpressionElement::new(expression, context).fmt(f)?; } } - } else { - FormatLiteralContent { - range: part.content_range(), - flags: self.flags, - is_fstring: true, - trim_end: false, - trim_start: false, - } - .fmt(f)?; } } } @@ -386,9 +348,6 @@ impl Format> for FormatLiteralContent { 0, self.flags, self.flags.is_f_string() && !self.is_fstring, - // TODO: Remove the argument from `normalize_string` when promoting the `is_f_string_formatting_enabled` preview style. - self.flags.is_f_string() && !is_f_string_formatting_enabled(f.context()), - is_f_string_formatting_enabled(f.context()), ); // Trim the start and end of the string if it's the first or last part of a docstring. @@ -413,7 +372,7 @@ impl Format> for FormatLiteralContent { Cow::Owned(normalized) => text(normalized).fmt(f)?, } - if self.trim_end && needs_chaperone_space(self.flags, &normalized, f.context()) { + if self.trim_end && needs_chaperone_space(self.flags, &normalized) { space().fmt(f)?; } } diff --git a/crates/ruff_python_formatter/src/string/mod.rs b/crates/ruff_python_formatter/src/string/mod.rs index 9f76aee3c7..ca4e59fda3 100644 --- a/crates/ruff_python_formatter/src/string/mod.rs +++ b/crates/ruff_python_formatter/src/string/mod.rs @@ -10,22 +10,13 @@ use ruff_python_ast::{ use ruff_source_file::LineRanges; use ruff_text_size::Ranged; -use crate::expression::expr_f_string::f_string_quoting; use crate::prelude::*; -use crate::preview::is_f_string_formatting_enabled; use crate::QuoteStyle; pub(crate) mod docstring; pub(crate) mod implicit; mod normalize; -#[derive(Copy, Clone, Debug, Default)] -pub(crate) enum Quoting { - #[default] - CanChange, - Preserve, -} - impl Format> for AnyStringPrefix { fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> { // Remove the unicode prefix `u` if any because it is meaningless in Python 3+. @@ -90,19 +81,10 @@ impl From for QuoteStyle { // Extension trait that adds formatter specific helper methods to `StringLike`. pub(crate) trait StringLikeExtensions { - fn quoting(&self, source: &str) -> Quoting; - fn is_multiline(&self, context: &PyFormatContext) -> bool; } impl StringLikeExtensions for ast::StringLike<'_> { - fn quoting(&self, source: &str) -> Quoting { - match self { - Self::String(_) | Self::Bytes(_) => Quoting::CanChange, - Self::FString(f_string) => f_string_quoting(f_string, source), - } - } - fn is_multiline(&self, context: &PyFormatContext) -> bool { self.parts().any(|part| match part { StringLikePart::String(_) | StringLikePart::Bytes(_) => { @@ -149,15 +131,11 @@ impl StringLikeExtensions for ast::StringLike<'_> { }) } - if is_f_string_formatting_enabled(context) { - contains_line_break_or_comments( - &f_string.elements, - context, - f_string.flags.is_triple_quoted(), - ) - } else { - context.source().contains_line_break(f_string.range()) - } + contains_line_break_or_comments( + &f_string.elements, + context, + f_string.flags.is_triple_quoted(), + ) } }) } diff --git a/crates/ruff_python_formatter/src/string/normalize.rs b/crates/ruff_python_formatter/src/string/normalize.rs index 2606fd5e95..5632324511 100644 --- a/crates/ruff_python_formatter/src/string/normalize.rs +++ b/crates/ruff_python_formatter/src/string/normalize.rs @@ -12,12 +12,10 @@ use ruff_text_size::{Ranged, TextRange, TextSlice}; use crate::context::FStringState; use crate::prelude::*; -use crate::preview::is_f_string_formatting_enabled; -use crate::string::{Quoting, StringQuotes}; +use crate::string::StringQuotes; use crate::QuoteStyle; pub(crate) struct StringNormalizer<'a, 'src> { - quoting: Quoting, preferred_quote_style: Option, context: &'a PyFormatContext<'src>, } @@ -25,7 +23,6 @@ pub(crate) struct StringNormalizer<'a, 'src> { impl<'a, 'src> StringNormalizer<'a, 'src> { pub(crate) fn from_context(context: &'a PyFormatContext<'src>) -> Self { Self { - quoting: Quoting::default(), preferred_quote_style: None, context, } @@ -36,11 +33,6 @@ impl<'a, 'src> StringNormalizer<'a, 'src> { self } - pub(crate) fn with_quoting(mut self, quoting: Quoting) -> Self { - self.quoting = quoting; - self - } - /// Determines the preferred quote style for `string`. /// The formatter should use the preferred quote style unless /// it can't because the string contains the preferred quotes OR @@ -49,112 +41,106 @@ impl<'a, 'src> StringNormalizer<'a, 'src> { /// Note: If you add more cases here where we return `QuoteStyle::Preserve`, /// make sure to also add them to [`FormatImplicitConcatenatedStringFlat::new`]. pub(super) fn preferred_quote_style(&self, string: StringLikePart) -> QuoteStyle { - match self.quoting { - Quoting::Preserve => QuoteStyle::Preserve, - Quoting::CanChange => { - let preferred_quote_style = self - .preferred_quote_style - .unwrap_or(self.context.options().quote_style()); + let preferred_quote_style = self + .preferred_quote_style + .unwrap_or(self.context.options().quote_style()); - if preferred_quote_style.is_preserve() { + if preferred_quote_style.is_preserve() { + return QuoteStyle::Preserve; + } + + if let StringLikePart::FString(fstring) = string { + // 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. + if !self.context.options().target_version().supports_pep_701() { + // An f-string expression contains a debug text with a quote character + // because the formatter will emit the debug expression **exactly** the + // same as in the source text. + if is_fstring_with_quoted_debug_expression(fstring, self.context) { return QuoteStyle::Preserve; } - if let StringLikePart::FString(fstring) = string { - // 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. - if !self.context.options().target_version().supports_pep_701() { - // An f-string expression contains a debug text with a quote character - // because the formatter will emit the debug expression **exactly** the - // same as in the source text. - if is_fstring_with_quoted_debug_expression(fstring, self.context) { - return QuoteStyle::Preserve; - } - - // An f-string expression that contains a triple quoted string literal - // expression that contains a quote. - if is_fstring_with_triple_quoted_literal_expression_containing_quotes( - fstring, - self.context, - ) { - return QuoteStyle::Preserve; - } - } - - // An f-string expression element contains a debug text and the corresponding - // format specifier has a literal element with a quote character. - if is_fstring_with_quoted_format_spec_and_debug(fstring, self.context) { - return QuoteStyle::Preserve; - } - } - - // 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. - if string.flags().is_triple_quoted() { - // ... unless we're formatting a code snippet inside a docstring, - // then we specifically want to invert our quote style to avoid - // writing out invalid Python. - // - // It's worth pointing out that we can actually wind up being - // somewhat out of sync with PEP8 in this case. Consider this - // example: - // - // def foo(): - // ''' - // Something. - // - // >>> """tricksy""" - // ''' - // pass - // - // Ideally, this would be reformatted as: - // - // def foo(): - // """ - // Something. - // - // >>> '''tricksy''' - // """ - // pass - // - // But the logic here results in the original quoting being - // preserved. This is because the quoting style of the outer - // docstring is determined, in part, by looking at its contents. In - // this case, it notices that it contains a `"""` and thus infers - // that using `'''` would overall read better because it avoids - // the need to escape the interior `"""`. Except... in this case, - // the `"""` is actually part of a code snippet that could get - // reformatted to using a different quoting style itself. - // - // Fixing this would, I believe, require some fairly seismic - // changes to how formatting strings works. Namely, we would need - // to look for code snippets before normalizing the docstring, and - // then figure out the quoting style more holistically by looking - // at the various kinds of quotes used in the code snippets and - // what reformatting them might look like. - // - // Overall this is a bit of a corner case and just inverting the - // style from what the parent ultimately decided upon works, even - // if it doesn't have perfect alignment with PEP8. - if let Some(quote) = self.context.docstring() { - QuoteStyle::from(quote.opposite()) - } else { - QuoteStyle::Double - } - } else { - preferred_quote_style + // An f-string expression that contains a triple quoted string literal + // expression that contains a quote. + if is_fstring_with_triple_quoted_literal_expression_containing_quotes( + fstring, + self.context, + ) { + return QuoteStyle::Preserve; } } + + // An f-string expression element contains a debug text and the corresponding + // format specifier has a literal element with a quote character. + if is_fstring_with_quoted_format_spec_and_debug(fstring, self.context) { + return QuoteStyle::Preserve; + } + } + + // 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. + if string.flags().is_triple_quoted() { + // ... unless we're formatting a code snippet inside a docstring, + // then we specifically want to invert our quote style to avoid + // writing out invalid Python. + // + // It's worth pointing out that we can actually wind up being + // somewhat out of sync with PEP8 in this case. Consider this + // example: + // + // def foo(): + // ''' + // Something. + // + // >>> """tricksy""" + // ''' + // pass + // + // Ideally, this would be reformatted as: + // + // def foo(): + // """ + // Something. + // + // >>> '''tricksy''' + // """ + // pass + // + // But the logic here results in the original quoting being + // preserved. This is because the quoting style of the outer + // docstring is determined, in part, by looking at its contents. In + // this case, it notices that it contains a `"""` and thus infers + // that using `'''` would overall read better because it avoids + // the need to escape the interior `"""`. Except... in this case, + // the `"""` is actually part of a code snippet that could get + // reformatted to using a different quoting style itself. + // + // Fixing this would, I believe, require some fairly seismic + // changes to how formatting strings works. Namely, we would need + // to look for code snippets before normalizing the docstring, and + // then figure out the quoting style more holistically by looking + // at the various kinds of quotes used in the code snippets and + // what reformatting them might look like. + // + // Overall this is a bit of a corner case and just inverting the + // style from what the parent ultimately decided upon works, even + // if it doesn't have perfect alignment with PEP8. + if let Some(quote) = self.context.docstring() { + QuoteStyle::from(quote.opposite()) + } else { + QuoteStyle::Double + } + } else { + preferred_quote_style } } @@ -163,7 +149,7 @@ impl<'a, 'src> StringNormalizer<'a, 'src> { let raw_content = &self.context.source()[string.content_range()]; let first_quote_or_normalized_char_offset = raw_content .bytes() - .position(|b| matches!(b, b'\\' | b'"' | b'\'' | b'\r' | b'{')); + .position(|b| matches!(b, b'\\' | b'"' | b'\'' | b'\r')); let string_flags = string.flags(); let preferred_style = self.preferred_quote_style(string); @@ -214,11 +200,7 @@ impl<'a, 'src> StringNormalizer<'a, 'src> { raw_content, first_quote_or_escape_offset, quote_selection.flags, - // TODO: Remove the `b'{'` in `choose_quotes` when promoting the - // `format_fstring` preview style false, - false, - is_f_string_formatting_enabled(self.context), ) } else { Cow::Borrowed(raw_content) @@ -269,20 +251,14 @@ impl QuoteMetadata { Self::from_str(text, part.flags(), preferred_quote) } StringLikePart::FString(fstring) => { - if is_f_string_formatting_enabled(context) { - let metadata = QuoteMetadata::from_str("", part.flags(), preferred_quote); + let metadata = QuoteMetadata::from_str("", part.flags(), preferred_quote); - metadata.merge_fstring_elements( - &fstring.elements, - fstring.flags, - context, - preferred_quote, - ) - } else { - let text = &context.source()[part.content_range()]; - - Self::from_str(text, part.flags(), preferred_quote) - } + metadata.merge_fstring_elements( + &fstring.elements, + fstring.flags, + context, + preferred_quote, + ) } } } @@ -635,8 +611,6 @@ pub(crate) fn normalize_string( start_offset: usize, new_flags: AnyStringFlags, escape_braces: bool, - flip_nested_fstring_quotes: bool, - format_f_string: bool, ) -> Cow { // The normalized string if `input` is not yet normalized. // `output` must remain empty if `input` is already normalized. @@ -653,9 +627,6 @@ pub(crate) fn normalize_string( let is_raw = new_flags.is_raw_string(); - let is_fstring = !format_f_string && new_flags.is_f_string(); - let mut formatted_value_nesting = 0u32; - while let Some((index, c)) = chars.next() { if matches!(c, '{' | '}') { if escape_braces { @@ -664,17 +635,6 @@ pub(crate) fn normalize_string( output.push(c); last_index = index + c.len_utf8(); continue; - } else if is_fstring { - if chars.peek().copied().is_some_and(|(_, next)| next == c) { - // Skip over the second character of the double braces - chars.next(); - } else if c == '{' { - formatted_value_nesting += 1; - } else { - // Safe to assume that `c == '}'` here because of the matched pattern above - formatted_value_nesting = formatted_value_nesting.saturating_sub(1); - } - continue; } } @@ -723,7 +683,7 @@ pub(crate) fn normalize_string( if !new_flags.is_triple_quoted() { #[allow(clippy::if_same_then_else)] - if next == opposite_quote && formatted_value_nesting == 0 { + if next == opposite_quote { // Remove the escape by ending before the backslash and starting again with the quote chars.next(); output.push_str(&input[last_index..index]); @@ -734,23 +694,12 @@ pub(crate) fn normalize_string( } } } - } else if !new_flags.is_triple_quoted() - && c == preferred_quote - && formatted_value_nesting == 0 - { + } else if !new_flags.is_triple_quoted() && c == preferred_quote { // Escape the quote output.push_str(&input[last_index..index]); output.push('\\'); output.push(c); last_index = index + preferred_quote.len_utf8(); - } else if c == preferred_quote - && flip_nested_fstring_quotes - && formatted_value_nesting > 0 - { - // Flip the quotes - output.push_str(&input[last_index..index]); - output.push(opposite_quote); - last_index = index + preferred_quote.len_utf8(); } } } @@ -1099,7 +1048,6 @@ fn contains_opposite_quote(content: &str, flags: AnyStringFlags) -> bool { mod tests { use std::borrow::Cow; - use ruff_python_ast::str_prefix::FStringPrefix; use ruff_python_ast::{ str::Quote, str_prefix::{AnyStringPrefix, ByteStringPrefix}, @@ -1133,34 +1081,8 @@ mod tests { false, ), false, - false, - true, ); assert_eq!(r"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", &normalized); } - - #[test] - fn normalize_nested_fstring() { - let input = - r#"With single quote: ' {my_dict['foo']} With double quote: " {my_dict["bar"]}"#; - - let normalized = normalize_string( - input, - 0, - AnyStringFlags::new( - AnyStringPrefix::Format(FStringPrefix::Regular), - Quote::Double, - false, - ), - false, - true, - false, - ); - - assert_eq!( - "With single quote: ' {my_dict['foo']} With double quote: \\\" {my_dict['bar']}", - &normalized - ); - } } diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition.py.snap index 2d2de804b5..17b2ace0e4 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition.py.snap @@ -194,41 +194,7 @@ class C: ```diff --- Black +++ Ruff -@@ -110,19 +110,20 @@ - value, is_going_to_be="too long to fit in a single line", srsly=True - ), "Not what we expected" - -- assert { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } == expected, ( -- "Not what we expected and the message is too long to fit in one line" -- ) -+ assert ( -+ { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ == expected -+ ), "Not what we expected and the message is too long to fit in one line" - - assert expected( - value, is_going_to_be="too long to fit in a single line", srsly=True -@@ -161,9 +162,7 @@ +@@ -161,9 +161,7 @@ 8 STORE_ATTR 0 (x) 10 LOAD_CONST 0 (None) 12 RETURN_VALUE @@ -356,20 +322,19 @@ class C: value, is_going_to_be="too long to fit in a single line", srsly=True ), "Not what we expected" - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected - ), "Not what we expected and the message is too long to fit in one line" + assert { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9, + } == expected, ( + "Not what we expected and the message is too long to fit in one line" + ) assert expected( value, is_going_to_be="too long to fit in a single line", srsly=True diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition_no_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition_no_trailing_comma.py.snap index 2a60999cd1..b49c9b9f6d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition_no_trailing_comma.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition_no_trailing_comma.py.snap @@ -194,41 +194,7 @@ class C: ```diff --- Black +++ Ruff -@@ -110,19 +110,20 @@ - value, is_going_to_be="too long to fit in a single line", srsly=True - ), "Not what we expected" - -- assert { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } == expected, ( -- "Not what we expected and the message is too long to fit in one line" -- ) -+ assert ( -+ { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } -+ == expected -+ ), "Not what we expected and the message is too long to fit in one line" - - assert expected( - value, is_going_to_be="too long to fit in a single line", srsly=True -@@ -161,9 +162,7 @@ +@@ -161,9 +161,7 @@ 8 STORE_ATTR 0 (x) 10 LOAD_CONST 0 (None) 12 RETURN_VALUE @@ -356,20 +322,19 @@ class C: value, is_going_to_be="too long to fit in a single line", srsly=True ), "Not what we expected" - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected - ), "Not what we expected and the message is too long to fit in one line" + assert { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9, + } == expected, ( + "Not what we expected and the message is too long to fit in one line" + ) assert expected( value, is_going_to_be="too long to fit in a single line", srsly=True diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__expression.py.snap index 81893524d0..428e8065f5 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__expression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__expression.py.snap @@ -276,6 +276,19 @@ last_call() ) # note: no trailing comma pre-3.6 call(*gidgets[:2]) call(a, *gidgets[:2]) +@@ -251,9 +251,9 @@ + print(**{1: 3} if False else {x: x for x in range(3)}) + print(*lambda x: x) + assert not Test, "Short message" +-assert this is ComplexTest and not requirements.fit_in_a_single_line( +- force=False +-), "Short message" ++assert this is ComplexTest and not requirements.fit_in_a_single_line(force=False), ( ++ "Short message" ++) + assert parens is TooMany + for (x,) in (1,), (2,), (3,): + ... ``` ## Ruff Output @@ -534,9 +547,9 @@ print(*[] or [1]) print(**{1: 3} if False else {x: x for x in range(3)}) print(*lambda x: x) assert not Test, "Short message" -assert this is ComplexTest and not requirements.fit_in_a_single_line( - force=False -), "Short message" +assert this is ComplexTest and not requirements.fit_in_a_single_line(force=False), ( + "Short message" +) assert parens is TooMany for (x,) in (1,), (2,), (3,): ... diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__f_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__f_docstring.py.snap new file mode 100644 index 0000000000..d23137efd4 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__f_docstring.py.snap @@ -0,0 +1,66 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/f_docstring.py +--- +## Input + +```python +def foo(e): + f""" {'.'.join(e)}""" + +def bar(e): + f"{'.'.join(e)}" + +def baz(e): + F""" {'.'.join(e)}""" +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,5 +1,5 @@ + def foo(e): +- f""" {'.'.join(e)}""" ++ f""" {".".join(e)}""" + + + def bar(e): +@@ -7,4 +7,4 @@ + + + def baz(e): +- f""" {'.'.join(e)}""" ++ f""" {".".join(e)}""" +``` + +## Ruff Output + +```python +def foo(e): + f""" {".".join(e)}""" + + +def bar(e): + f"{'.'.join(e)}" + + +def baz(e): + f""" {".".join(e)}""" +``` + +## Black Output + +```python +def foo(e): + f""" {'.'.join(e)}""" + + +def bar(e): + f"{'.'.join(e)}" + + +def baz(e): + f""" {'.'.join(e)}""" +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap index b19050217c..4d5f61df8e 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap @@ -105,7 +105,7 @@ elif unformatted: - "=foo.bar.:main", - # fmt: on - ] # Includes an formatted indentation. -+ "foo-bar" "=foo.bar.:main", ++ "foo-bar=foo.bar.:main", + # fmt: on + ] # Includes an formatted indentation. }, @@ -129,7 +129,7 @@ setup( entry_points={ # fmt: off "console_scripts": [ - "foo-bar" "=foo.bar.:main", + "foo-bar=foo.bar.:main", # fmt: on ] # Includes an formatted indentation. }, diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring.py.snap new file mode 100644 index 0000000000..82def26815 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring.py.snap @@ -0,0 +1,74 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.py +--- +## Input + +```python +f"f-string without formatted values is just a string" +f"{{NOT a formatted value}}" +f"{{NOT 'a' \"formatted\" \"value\"}}" +f"some f-string with {a} {few():.2f} {formatted.values!r}" +f'some f-string with {a} {few(""):.2f} {formatted.values!r}' +f"{f'''{'nested'} inner'''} outer" +f"\"{f'{nested} inner'}\" outer" +f"space between opening braces: { {a for a in (1, 2, 3)}}" +f'Hello \'{tricky + "example"}\'' +f"Tried directories {str(rootdirs)} \ +but none started with prefix {parentdir_prefix}" +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -2,10 +2,10 @@ + f"{{NOT a formatted value}}" + f'{{NOT \'a\' "formatted" "value"}}' + f"some f-string with {a} {few():.2f} {formatted.values!r}" +-f'some f-string with {a} {few(""):.2f} {formatted.values!r}' +-f"{f'''{'nested'} inner'''} outer" +-f"\"{f'{nested} inner'}\" outer" +-f"space between opening braces: { {a for a in (1, 2, 3)}}" +-f'Hello \'{tricky + "example"}\'' ++f"some f-string with {a} {few(''):.2f} {formatted.values!r}" ++f"{f'''{"nested"} inner'''} outer" ++f'"{f"{nested} inner"}" outer' ++f"space between opening braces: { {a for a in (1, 2, 3)} }" ++f"Hello '{tricky + 'example'}'" + f"Tried directories {str(rootdirs)} \ + but none started with prefix {parentdir_prefix}" +``` + +## Ruff Output + +```python +f"f-string without formatted values is just a string" +f"{{NOT a formatted value}}" +f'{{NOT \'a\' "formatted" "value"}}' +f"some f-string with {a} {few():.2f} {formatted.values!r}" +f"some f-string with {a} {few(''):.2f} {formatted.values!r}" +f"{f'''{"nested"} inner'''} outer" +f'"{f"{nested} inner"}" outer' +f"space between opening braces: { {a for a in (1, 2, 3)} }" +f"Hello '{tricky + 'example'}'" +f"Tried directories {str(rootdirs)} \ +but none started with prefix {parentdir_prefix}" +``` + +## Black Output + +```python +f"f-string without formatted values is just a string" +f"{{NOT a formatted value}}" +f'{{NOT \'a\' "formatted" "value"}}' +f"some f-string with {a} {few():.2f} {formatted.values!r}" +f'some f-string with {a} {few(""):.2f} {formatted.values!r}' +f"{f'''{'nested'} inner'''} outer" +f"\"{f'{nested} inner'}\" outer" +f"space between opening braces: { {a for a in (1, 2, 3)}}" +f'Hello \'{tricky + "example"}\'' +f"Tried directories {str(rootdirs)} \ +but none started with prefix {parentdir_prefix}" +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function_trailing_comma.py.snap index 62a169b1b0..0cb75bf9be 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function_trailing_comma.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function_trailing_comma.py.snap @@ -132,6 +132,19 @@ variable: ( ```diff --- Black +++ Ruff +@@ -52,9 +52,9 @@ + pass + + +-def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( +- Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +-): ++def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ ++ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ++]: + json = { + "k": { + "k2": { @@ -130,9 +130,7 @@ def foo() -> ( @@ -225,9 +238,9 @@ def f( pass -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( - Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] -): +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: json = { "k": { "k2": { diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap index 4435d34dcb..dc0aecbac2 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap @@ -309,33 +309,52 @@ long_unmergable_string_with_pragma = ( ```diff --- Black +++ Ruff -@@ -167,13 +167,9 @@ +@@ -167,14 +167,14 @@ triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" -assert ( - some_type_of_boolean_expression -), "Followed by a really really really long string that is used to provide context to the AssertionError exception." -+assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception." ++assert some_type_of_boolean_expression, ( ++ "Followed by a really really really long string that is used to provide context to the AssertionError exception." ++) -assert ( - some_type_of_boolean_expression -), "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format( -+assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format( - "formatting" +- "formatting" ++assert some_type_of_boolean_expression, ( ++ "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format( ++ "formatting" ++ ) ) -@@ -256,9 +252,7 @@ + assert some_type_of_boolean_expression, ( +@@ -255,10 +255,8 @@ + + " that has been " + CONCATENATED + "using the '+' operator." - ) +-) -annotated_variable: Final = ( - "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." --) + ) +annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." annotated_variable: Literal["fakse_literal"] = ( "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." ) +@@ -267,9 +265,9 @@ + backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" + backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\" + +-short_string = "Hi" " there." ++short_string = "Hi there." + +-func_call(short_string=("Hi" " there.")) ++func_call(short_string=("Hi there.")) + + raw_strings = r"Don't" " get" r" merged" " unless they are all raw." + ``` ## Ruff Output @@ -510,10 +529,14 @@ pragma_comment_string2 = "Lines which end with an inline pragma comment of the f triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" -assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception." +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to the AssertionError exception." +) -assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format( - "formatting" +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format( + "formatting" + ) ) assert some_type_of_boolean_expression, ( @@ -604,9 +627,9 @@ backslashes = "This is a really long string with \"embedded\" double quotes and backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\" -short_string = "Hi" " there." +short_string = "Hi there." -func_call(short_string=("Hi" " there.")) +func_call(short_string=("Hi there.")) raw_strings = r"Don't" " get" r" merged" " unless they are all raw." diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_remove_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_remove_parens.py.snap new file mode 100644 index 0000000000..08e513bc17 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_remove_parens.py.snap @@ -0,0 +1,241 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py +--- +## Input + +```python +if (foo := 0): + pass + +if (foo := 1): + pass + +if (y := 5 + 5): + pass + +y = (x := 0) + +y += (x := 0) + +(y := 5 + 5) + +test: int = (test2 := 2) + +a, b = (test := (1, 2)) + +# see also https://github.com/psf/black/issues/2139 +assert (foo := 42 - 12) + +foo(x=(y := f(x))) + + +def foo(answer=(p := 42)): + ... + + +def foo2(answer: (p := 42) = 5): + ... + + +lambda: (x := 1) + +a[(x := 12)] +a[:(x := 13)] + +# we don't touch expressions in f-strings but if we do one day, don't break 'em +f'{(x:=10)}' + + +def a(): + return (x := 3) + await (b := 1) + yield (a := 2) + raise (c := 3) + +def this_is_so_dumb() -> (please := no): + pass + +async def await_the_walrus(): + with (x := y): + pass + + with (x := y) as z, (a := b) as c: + pass + + with (x := await y): + pass + + with (x := await a, y := await b): + pass + + with ((x := await a, y := await b)): + pass + + with (x := await a), (y := await b): + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -35,7 +35,7 @@ + a[: (x := 13)] + + # we don't touch expressions in f-strings but if we do one day, don't break 'em +-f"{(x:=10)}" ++f"{(x := 10)}" + + + def a(): +``` + +## Ruff Output + +```python +if foo := 0: + pass + +if foo := 1: + pass + +if y := 5 + 5: + pass + +y = (x := 0) + +y += (x := 0) + +(y := 5 + 5) + +test: int = (test2 := 2) + +a, b = (test := (1, 2)) + +# see also https://github.com/psf/black/issues/2139 +assert (foo := 42 - 12) + +foo(x=(y := f(x))) + + +def foo(answer=(p := 42)): ... + + +def foo2(answer: (p := 42) = 5): ... + + +lambda: (x := 1) + +a[(x := 12)] +a[: (x := 13)] + +# we don't touch expressions in f-strings but if we do one day, don't break 'em +f"{(x := 10)}" + + +def a(): + return (x := 3) + await (b := 1) + yield (a := 2) + raise (c := 3) + + +def this_is_so_dumb() -> (please := no): + pass + + +async def await_the_walrus(): + with (x := y): + pass + + with (x := y) as z, (a := b) as c: + pass + + with (x := await y): + pass + + with (x := await a, y := await b): + pass + + with (x := await a, y := await b): + pass + + with (x := await a), (y := await b): + pass +``` + +## Black Output + +```python +if foo := 0: + pass + +if foo := 1: + pass + +if y := 5 + 5: + pass + +y = (x := 0) + +y += (x := 0) + +(y := 5 + 5) + +test: int = (test2 := 2) + +a, b = (test := (1, 2)) + +# see also https://github.com/psf/black/issues/2139 +assert (foo := 42 - 12) + +foo(x=(y := f(x))) + + +def foo(answer=(p := 42)): ... + + +def foo2(answer: (p := 42) = 5): ... + + +lambda: (x := 1) + +a[(x := 12)] +a[: (x := 13)] + +# we don't touch expressions in f-strings but if we do one day, don't break 'em +f"{(x:=10)}" + + +def a(): + return (x := 3) + await (b := 1) + yield (a := 2) + raise (c := 3) + + +def this_is_so_dumb() -> (please := no): + pass + + +async def await_the_walrus(): + with (x := y): + pass + + with (x := y) as z, (a := b) as c: + pass + + with (x := await y): + pass + + with (x := await a, y := await b): + pass + + with (x := await a, y := await b): + pass + + with (x := await a), (y := await b): + pass +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap index 649bb0ea5f..1b074e6b45 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap @@ -149,15 +149,155 @@ rf"\{"a"}" ```diff --- Black +++ Ruff -@@ -119,7 +119,7 @@ +@@ -11,16 +11,14 @@ + # edge case: FSTRING_MIDDLE containing only whitespace should not be stripped + x = f"{a} {b}" + +-x = f"foo { +- 2 + 2 +-} bar baz" ++x = f"foo {2 + 2} bar baz" + +-x = f"foo {{ {"a {2 + 2} b"}bar {{ baz" ++x = f"foo {{ {'a {2 + 2} b'}bar {{ baz" ++x = f"foo {{ {f'a {2 + 2} b'}bar {{ baz" + x = f"foo {{ {f'a {2 + 2} b'}bar {{ baz" +-x = f"foo {{ {f"a {2 + 2} b"}bar {{ baz" + + x = f"foo {{ {f'a {f"a {2 + 2} b"} b'}bar {{ baz" +-x = f"foo {{ {f"a {f"a {2 + 2} b"} b"}bar {{ baz" ++x = f"foo {{ {f'a {f"a {2 + 2} b"} b'}bar {{ baz" + + x = """foo {{ {2 + 2}bar + baz""" +@@ -28,74 +26,62 @@ + + x = f"""foo {{ {2 + 2}bar {{ baz""" + +-x = f"""foo {{ { +- 2 + 2 +-}bar {{ baz""" ++x = f"""foo {{ {2 + 2}bar {{ baz""" + + +-x = f"""foo {{ { +- 2 + 2 +-}bar ++x = f"""foo {{ {2 + 2}bar + baz""" + + x = f"""foo {{ a + foo {2 + 2}bar {{ baz + + x = f"foo {{ { +- 2 + 2 # comment +- }bar" ++ 2 + 2 # comment ++}bar" + + {{ baz + + }} buzz + +- {print("abc" + "def" +-)} ++ {print("abc" + "def")} + abc""" + + # edge case: end triple quotes at index zero +-f"""foo {2+2} bar ++f"""foo {2 + 2} bar + """ + +-f' \' {f"'"} \' ' +-f" \" {f'"'} \" " ++f" ' {f"'"} ' " ++f' " {f'"'} " ' + +-x = f"a{2+2:=^72}b" +-x = f"a{2+2:x}b" ++x = f"a{2 + 2:=^72}b" ++x = f"a{2 + 2:x}b" + + rf"foo" + rf"{foo}" + + f"{x:{y}d}" + +-x = f"a{2+2:=^{x}}b" +-x = f"a{2+2:=^{foo(x+y**2):something else}}b" +-x = f"a{2+2:=^{foo(x+y**2):something else}one more}b" +-f"{(abc:=10)}" ++x = f"a{2 + 2:=^{x}}b" ++x = f"a{2 + 2:=^{foo(x + y**2):something else}}b" ++x = f"a{2 + 2:=^{foo(x + y**2):something else}one more}b" ++f"{(abc := 10)}" + +-f"This is a really long string, but just make sure that you reflow fstrings { +- 2+2:d +-}" +-f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}" ++f"This is a really long string, but just make sure that you reflow fstrings {2 + 2:d}" ++f"This is a really long string, but just make sure that you reflow fstrings correctly {2 + 2:d}" + + f"{2+2=}" + f"{2+2 = }" + f"{ 2 + 2 = }" + +-f"""foo { +- datetime.datetime.now():%Y ++f"""foo {datetime.datetime.now():%Y + %m + %d + }""" + +-f"{ +-X +-!r +-}" ++f"{X!r}" + + raise ValueError( +- "xxxxxxxxxxxIncorrect --line-ranges format, expect START-END, found" +- f" {lines_str!r}" ++ f"xxxxxxxxxxxIncorrect --line-ranges format, expect START-END, found {lines_str!r}" ) - f"{1:{f'{2}'}}" --f"{1:{f'{2}'}}" -+f'{1:{f'{2}'}}' - f"{1:{2}d}" + f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \ +@@ -105,8 +91,10 @@ + rf"\{{\}}" + + f""" +- WITH {f''' +- {1}_cte AS ()'''} ++ WITH { ++ f''' ++ {1}_cte AS ()''' ++} + """ + + value: str = f"""foo +@@ -124,13 +112,15 @@ f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}' + +-f"""{''' +-'''}""" ++f"""{ ++ ''' ++''' ++}""" + +-f"{'\''}" +-f"{f'\''}" ++f"{"'"}" ++f"{f"'"}" + + f"{1}\{{" + f"{2} foo \{{[\}}" + f"\{3}" +-rf"\{"a"}" ++rf"\{'a'}" ``` ## Ruff Output @@ -176,16 +316,14 @@ x = f"""foo {{ {2 + 2}bar {{ baz""" # edge case: FSTRING_MIDDLE containing only whitespace should not be stripped x = f"{a} {b}" -x = f"foo { - 2 + 2 -} bar baz" +x = f"foo {2 + 2} bar baz" -x = f"foo {{ {"a {2 + 2} b"}bar {{ baz" +x = f"foo {{ {'a {2 + 2} b'}bar {{ baz" +x = f"foo {{ {f'a {2 + 2} b'}bar {{ baz" x = f"foo {{ {f'a {2 + 2} b'}bar {{ baz" -x = f"foo {{ {f"a {2 + 2} b"}bar {{ baz" x = f"foo {{ {f'a {f"a {2 + 2} b"} b'}bar {{ baz" -x = f"foo {{ {f"a {f"a {2 + 2} b"} b"}bar {{ baz" +x = f"foo {{ {f'a {f"a {2 + 2} b"} b'}bar {{ baz" x = """foo {{ {2 + 2}bar baz""" @@ -193,74 +331,62 @@ baz""" x = f"""foo {{ {2 + 2}bar {{ baz""" -x = f"""foo {{ { - 2 + 2 -}bar {{ baz""" +x = f"""foo {{ {2 + 2}bar {{ baz""" -x = f"""foo {{ { - 2 + 2 -}bar +x = f"""foo {{ {2 + 2}bar baz""" x = f"""foo {{ a foo {2 + 2}bar {{ baz x = f"foo {{ { - 2 + 2 # comment - }bar" + 2 + 2 # comment +}bar" {{ baz }} buzz - {print("abc" + "def" -)} + {print("abc" + "def")} abc""" # edge case: end triple quotes at index zero -f"""foo {2+2} bar +f"""foo {2 + 2} bar """ -f' \' {f"'"} \' ' -f" \" {f'"'} \" " +f" ' {f"'"} ' " +f' " {f'"'} " ' -x = f"a{2+2:=^72}b" -x = f"a{2+2:x}b" +x = f"a{2 + 2:=^72}b" +x = f"a{2 + 2:x}b" rf"foo" rf"{foo}" f"{x:{y}d}" -x = f"a{2+2:=^{x}}b" -x = f"a{2+2:=^{foo(x+y**2):something else}}b" -x = f"a{2+2:=^{foo(x+y**2):something else}one more}b" -f"{(abc:=10)}" +x = f"a{2 + 2:=^{x}}b" +x = f"a{2 + 2:=^{foo(x + y**2):something else}}b" +x = f"a{2 + 2:=^{foo(x + y**2):something else}one more}b" +f"{(abc := 10)}" -f"This is a really long string, but just make sure that you reflow fstrings { - 2+2:d -}" -f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}" +f"This is a really long string, but just make sure that you reflow fstrings {2 + 2:d}" +f"This is a really long string, but just make sure that you reflow fstrings correctly {2 + 2:d}" f"{2+2=}" f"{2+2 = }" f"{ 2 + 2 = }" -f"""foo { - datetime.datetime.now():%Y +f"""foo {datetime.datetime.now():%Y %m %d }""" -f"{ -X -!r -}" +f"{X!r}" raise ValueError( - "xxxxxxxxxxxIncorrect --line-ranges format, expect START-END, found" - f" {lines_str!r}" + f"xxxxxxxxxxxIncorrect --line-ranges format, expect START-END, found {lines_str!r}" ) f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \ @@ -270,8 +396,10 @@ x = f"\N{GREEK CAPITAL LETTER DELTA} \N{SNOWMAN} {x}" rf"\{{\}}" f""" - WITH {f''' - {1}_cte AS ()'''} + WITH { + f''' + {1}_cte AS ()''' +} """ value: str = f"""foo @@ -284,21 +412,23 @@ log( ) f"{1:{f'{2}'}}" -f'{1:{f'{2}'}}' +f"{1:{f'{2}'}}" f"{1:{2}d}" f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}' -f"""{''' -'''}""" +f"""{ + ''' +''' +}""" -f"{'\''}" -f"{f'\''}" +f"{"'"}" +f"{f"'"}" f"{1}\{{" f"{2} foo \{{[\}}" f"\{3}" -rf"\{"a"}" +rf"\{'a'}" ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap index ea9f587ded..4737f38527 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap @@ -127,39 +127,7 @@ def foo(a,b) -> tuple[int, int, int,]: return 2 * a -@@ -99,25 +107,31 @@ - return 2 - - --def foo() -> tuple[ -- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, --]: -+def foo() -> ( -+ tuple[ -+ loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -+ loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -+ loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, -+ ] -+): - return 2 - - - # Magic trailing comma example --def foo() -> tuple[ -- int, -- int, -- int, --]: -+def foo() -> ( -+ tuple[ -+ int, -+ int, -+ int, -+ ] -+): - return 2 +@@ -117,7 +125,9 @@ # Magic trailing comma example, with params @@ -284,24 +252,20 @@ def foo() -> tuple[int, int, int]: return 2 -def foo() -> ( - tuple[ - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - ] -): +def foo() -> tuple[ + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, +]: return 2 # Magic trailing comma example -def foo() -> ( - tuple[ - int, - int, - int, - ] -): +def foo() -> tuple[ + int, + int, + int, +]: return 2 diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__trailing_commas_in_leading_parts.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__trailing_commas_in_leading_parts.py.snap index 6389a46477..7c3a12698a 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__trailing_commas_in_leading_parts.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__trailing_commas_in_leading_parts.py.snap @@ -64,11 +64,9 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( -assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx( - xxxxxxxxx -).xxxxxxxxxxxxxxxxxx(), ( -- "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" --) -+assert ( -+ xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(xxxxxxxxx).xxxxxxxxxxxxxxxxxx() -+), "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ++assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(xxxxxxxxx).xxxxxxxxxxxxxxxxxx(), ( + "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + ) ``` ## Ruff Output @@ -117,9 +115,9 @@ assert ( ) # Regression test for https://github.com/psf/black/issues/3414. -assert ( - xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(xxxxxxxxx).xxxxxxxxxxxxxxxxxx() -), "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(xxxxxxxxx).xxxxxxxxxxxxxxxxxx(), ( + "xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__string_quotes.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__string_quotes.py.snap index de54894a86..aa69aab3af 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__string_quotes.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__string_quotes.py.snap @@ -71,7 +71,15 @@ f"\"{a}\"{'hello' * b}\"{c}\"" ```diff --- Black +++ Ruff -@@ -25,7 +25,12 @@ +@@ -18,14 +18,19 @@ + quote""" + f"just a normal {f} string" + f"""This is a triple-quoted {f}-string""" +-f'MOAR {" ".join([])}' + f"MOAR {' '.join([])}" ++f"MOAR {' '.join([])}" + r"raw string ftw" + r"Date d\'expiration:(.*)" r'Tricky "quote' r"Not-so-tricky \"quote" rf"{yay}" @@ -85,6 +93,25 @@ f"\"{a}\"{'hello' * b}\"{c}\"" re.compile(r'[\\"]') "x = ''; y = \"\"" "x = '''; y = \"\"" +@@ -40,14 +45,14 @@ + '\\""' + "\\''" + "Lots of \\\\\\\\'quotes'" +-f'{y * " "} \'{z}\'' ++f"{y * ' '} '{z}'" + f"{{y * \" \"}} '{z}'" +-f'\'{z}\' {y * " "}' ++f"'{z}' {y * ' '}" + f"{y * x} '{z}'" + "'{z}' {y * \" \"}" + "{y * x} '{z}'" + + # We must bail out if changing the quotes would introduce backslashes in f-string + # expressions. xref: https://github.com/psf/black/issues/2348 +-f"\"{b}\"{' ' * (long-len(b)+1)}: \"{sts}\",\n" +-f"\"{a}\"{'hello' * b}\"{c}\"" ++f'"{b}"{" " * (long - len(b) + 1)}: "{sts}",\n' ++f'"{a}"{"hello" * b}"{c}"' ``` ## Ruff Output @@ -110,7 +137,7 @@ f"\"{a}\"{'hello' * b}\"{c}\"" quote""" f"just a normal {f} string" f"""This is a triple-quoted {f}-string""" -f'MOAR {" ".join([])}' +f"MOAR {' '.join([])}" f"MOAR {' '.join([])}" r"raw string ftw" r"Date d\'expiration:(.*)" @@ -137,17 +164,17 @@ re.compile(r'[\\"]') '\\""' "\\''" "Lots of \\\\\\\\'quotes'" -f'{y * " "} \'{z}\'' +f"{y * ' '} '{z}'" f"{{y * \" \"}} '{z}'" -f'\'{z}\' {y * " "}' +f"'{z}' {y * ' '}" f"{y * x} '{z}'" "'{z}' {y * \" \"}" "{y * x} '{z}'" # We must bail out if changing the quotes would introduce backslashes in f-string # expressions. xref: https://github.com/psf/black/issues/2348 -f"\"{b}\"{' ' * (long-len(b)+1)}: \"{sts}\",\n" -f"\"{a}\"{'hello' * b}\"{c}\"" +f'"{b}"{" " * (long - len(b) + 1)}: "{sts}",\n' +f'"{a}"{"hello" * b}"{c}"' ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap index 9e4114283a..ad87c9b969 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap @@ -616,7 +616,9 @@ def length_doctest(): Integer length of the list of numbers. Example: - >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + >>> length( + ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + ... ) 20 """ @@ -631,7 +633,9 @@ def length_doctest_underindent(): Integer length of the list of numbers. Example: - >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + >>> length( + ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + ... ) 20 """ @@ -649,7 +653,9 @@ def length_markdown(): Example: ``` - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) + length( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] + ) ``` """ @@ -659,7 +665,9 @@ def length_rst(): """ Do cool stuff:: - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) + length( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] + ) """ pass @@ -670,7 +678,9 @@ def length_rst_in_section(): Examples: Do cool stuff:: - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + length( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + ) """ pass ``` @@ -695,11 +705,13 @@ def length_rst_in_section(): """ -@@ -300,7 +302,28 @@ +@@ -300,9 +302,28 @@ Integer length of the list of numbers. Example: -- >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- >>> length( +- ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +- ... ) + >>> length([ + ... 1, + ... 2, @@ -725,11 +737,13 @@ def length_rst_in_section(): 20 """ -@@ -315,7 +338,28 @@ +@@ -317,9 +338,28 @@ Integer length of the list of numbers. Example: -- >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- >>> length( +- ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +- ... ) + >>> length([ + ... 1, + ... 2, @@ -755,11 +769,13 @@ def length_rst_in_section(): 20 """ -@@ -333,7 +377,29 @@ +@@ -337,9 +377,29 @@ Example: ``` -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) +- length( +- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] +- ) + length([ + 1, + 2, @@ -786,11 +802,13 @@ def length_rst_in_section(): ``` """ -@@ -343,7 +409,29 @@ +@@ -349,9 +409,29 @@ """ Do cool stuff:: -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) +- length( +- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] +- ) + length([ + 1, + 2, @@ -817,11 +835,13 @@ def length_rst_in_section(): """ pass -@@ -354,6 +442,27 @@ +@@ -362,8 +442,27 @@ Examples: Do cool stuff:: -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- length( +- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +- ) + length([ + 1, + 2, @@ -1157,7 +1177,9 @@ def length_doctest(): Integer length of the list of numbers. Example: - >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + >>> length( + ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + ... ) 20 """ @@ -1172,7 +1194,9 @@ def length_doctest_underindent(): Integer length of the list of numbers. Example: - >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + >>> length( + ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + ... ) 20 """ @@ -1190,7 +1214,9 @@ def length_markdown(): Example: ``` - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) + length( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] + ) ``` """ @@ -1200,7 +1226,9 @@ def length_rst(): """ Do cool stuff:: - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) + length( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] + ) """ pass @@ -1211,7 +1239,9 @@ def length_rst_in_section(): Examples: Do cool stuff:: - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + length( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + ) """ pass ``` @@ -1221,11 +1251,13 @@ def length_rst_in_section(): ```diff --- Stable +++ Preview -@@ -290,7 +290,28 @@ +@@ -290,9 +290,28 @@ Integer length of the list of numbers. Example: -- >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- >>> length( +- ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +- ... ) + >>> length([ + ... 1, + ... 2, @@ -1251,11 +1283,13 @@ def length_rst_in_section(): 20 """ -@@ -305,7 +326,28 @@ +@@ -307,9 +326,28 @@ Integer length of the list of numbers. Example: -- >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- >>> length( +- ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +- ... ) + >>> length([ + ... 1, + ... 2, @@ -1281,11 +1315,13 @@ def length_rst_in_section(): 20 """ -@@ -323,7 +365,29 @@ +@@ -327,9 +365,29 @@ Example: ``` -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) +- length( +- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] +- ) + length([ + 1, + 2, @@ -1312,11 +1348,13 @@ def length_rst_in_section(): ``` """ -@@ -333,7 +397,29 @@ +@@ -339,9 +397,29 @@ """ Do cool stuff:: -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) +- length( +- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] +- ) + length([ + 1, + 2, @@ -1343,11 +1381,13 @@ def length_rst_in_section(): """ pass -@@ -344,6 +430,27 @@ +@@ -352,8 +430,27 @@ Examples: Do cool stuff:: -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- length( +- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +- ) + length([ + 1, + 2, @@ -1693,7 +1733,9 @@ def length_doctest(): Integer length of the list of numbers. Example: - >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + >>> length( + ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + ... ) 20 """ @@ -1708,7 +1750,9 @@ def length_doctest_underindent(): Integer length of the list of numbers. Example: - >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + >>> length( + ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + ... ) 20 """ @@ -1726,7 +1770,9 @@ def length_markdown(): Example: ``` - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) + length( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] + ) ``` """ @@ -1736,7 +1782,9 @@ def length_rst(): """ Do cool stuff:: - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) + length( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] + ) """ pass @@ -1747,7 +1795,9 @@ def length_rst_in_section(): Examples: Do cool stuff:: - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + length( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + ) """ pass ``` @@ -1772,11 +1822,13 @@ def length_rst_in_section(): """ -@@ -300,7 +302,28 @@ +@@ -300,9 +302,28 @@ Integer length of the list of numbers. Example: -- >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- >>> length( +- ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +- ... ) + >>> length([ + ... 1, + ... 2, @@ -1802,11 +1854,13 @@ def length_rst_in_section(): 20 """ -@@ -315,7 +338,28 @@ +@@ -317,9 +338,28 @@ Integer length of the list of numbers. Example: -- >>> length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- >>> length( +- ... [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +- ... ) + >>> length([ + ... 1, + ... 2, @@ -1832,11 +1886,13 @@ def length_rst_in_section(): 20 """ -@@ -333,7 +377,29 @@ +@@ -337,9 +377,29 @@ Example: ``` -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) +- length( +- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] +- ) + length([ + 1, + 2, @@ -1863,11 +1919,13 @@ def length_rst_in_section(): ``` """ -@@ -343,7 +409,29 @@ +@@ -349,9 +409,29 @@ """ Do cool stuff:: -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]) +- length( +- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] +- ) + length([ + 1, + 2, @@ -1894,11 +1952,13 @@ def length_rst_in_section(): """ pass -@@ -354,6 +442,27 @@ +@@ -362,8 +442,27 @@ Examples: Do cool stuff:: -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- length( +- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] +- ) + length([ + 1, + 2, @@ -2822,7 +2882,30 @@ def length_rst_in_section(): Examples: Do cool stuff:: - length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + length( + [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + ] + ) """ pass ``` @@ -3063,11 +3146,34 @@ def length_rst_in_section(): """ pass -@@ -878,6 +872,27 @@ +@@ -878,29 +872,27 @@ Examples: Do cool stuff:: -- length([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) +- length( +- [ +- 1, +- 2, +- 3, +- 4, +- 5, +- 6, +- 7, +- 8, +- 9, +- 10, +- 11, +- 12, +- 13, +- 14, +- 15, +- 16, +- 17, +- 18, +- 19, +- 20, +- ] +- ) + length([ + 1, + 2, diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap index 8fa48c9f0a..9a5b9d1882 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary.py.snap @@ -911,52 +911,20 @@ if True: ) # This f-string should be flattened -xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa { - expression } bbbbbbbbbbbbbbbbbbbbbbbb" + (yyyyyyyyyyyyyy + zzzzzzzzzzz) +xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {expression} bbbbbbbbbbbbbbbbbbbbbbbb" + ( + yyyyyyyyyyyyyy + zzzzzzzzzzz +) # This is not a multiline f-string, but the expression is too long so it should be # wrapped in parentheses. -f"hellooooooooooooooooooooooo \ - worlddddddddddddddddddddddddddddddddd" + ( - aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb +( + f"hellooooooooooooooooooooooo \ + worlddddddddddddddddddddddddddddddddd" + + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) ) -aaaaaaaaaaa = f"hellooooooooooooooooooooooo \ - worlddddddddddddddddddddddddddddddddd" + ( - aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb +aaaaaaaaaaa = ( + f"hellooooooooooooooooooooooo \ + worlddddddddddddddddddddddddddddddddd" + + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) ) ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -468,16 +468,19 @@ - ) - - # This f-string should be flattened --xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa { -- expression } bbbbbbbbbbbbbbbbbbbbbbbb" + (yyyyyyyyyyyyyy + zzzzzzzzzzz) -+xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {expression} bbbbbbbbbbbbbbbbbbbbbbbb" + ( -+ yyyyyyyyyyyyyy + zzzzzzzzzzz -+) - - # This is not a multiline f-string, but the expression is too long so it should be - # wrapped in parentheses. --f"hellooooooooooooooooooooooo \ -- worlddddddddddddddddddddddddddddddddd" + ( -- aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb -+( -+ f"hellooooooooooooooooooooooo \ -+ worlddddddddddddddddddddddddddddddddd" -+ + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) - ) --aaaaaaaaaaa = f"hellooooooooooooooooooooooo \ -- worlddddddddddddddddddddddddddddddddd" + ( -- aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb -+aaaaaaaaaaa = ( -+ f"hellooooooooooooooooooooooo \ -+ worlddddddddddddddddddddddddddddddddd" -+ + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb) - ) -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary_implicit_string.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary_implicit_string.py.snap index 99b2eb9152..f05c74e3d1 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__binary_implicit_string.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__binary_implicit_string.py.snap @@ -399,27 +399,9 @@ c = ( "dddddddddddddddddddddddddd" % aaaaaaaaaaaa + x ) -"a" "b" "c" + "d" "e" + "f" "g" + "h" "i" "j" +"abc" + "de" + "fg" + "hij" class EC2REPATH: - f.write("Pathway name" + "\t" "Database Identifier" + "\t" "Source database" + "\n") -``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -197,8 +197,8 @@ - "dddddddddddddddddddddddddd" % aaaaaaaaaaaa + x - ) - --"a" "b" "c" + "d" "e" + "f" "g" + "h" "i" "j" -+"abc" + "de" + "fg" + "hij" - - - class EC2REPATH: -- f.write("Pathway name" + "\t" "Database Identifier" + "\t" "Source database" + "\n") -+ f.write("Pathway name" + "\tDatabase Identifier" + "\tSource database" + "\n") + f.write("Pathway name" + "\tDatabase Identifier" + "\tSource database" + "\n") ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap index cd075a0ee3..890767793c 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap @@ -211,9 +211,9 @@ String \"\"\" # String continuation -b"Let's" b"start" b"with" b"a" b"simple" b"example" +b"Let'sstartwithasimpleexample" -b"Let's" b"start" b"with" b"a" b"simple" b"example" b"now repeat after me:" b"I am confident" b"I am confident" b"I am confident" b"I am confident" b"I am confident" +b"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident" ( b"Let's" @@ -280,39 +280,12 @@ test_particular = [ ] # Parenthesized string continuation with messed up indentation -{"key": ([], b"a" b"b" b"c")} +{"key": ([], b"abc")} b"Unicode Escape sequence don't apply to bytes: \N{0x} \u{ABCD} \U{ABCDEFGH}" ``` -#### Preview changes -```diff ---- Stable -+++ Preview -@@ -63,9 +63,9 @@ - - # String continuation - --b"Let's" b"start" b"with" b"a" b"simple" b"example" -+b"Let'sstartwithasimpleexample" - --b"Let's" b"start" b"with" b"a" b"simple" b"example" b"now repeat after me:" b"I am confident" b"I am confident" b"I am confident" b"I am confident" b"I am confident" -+b"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident" - - ( - b"Let's" -@@ -132,6 +132,6 @@ - ] - - # Parenthesized string continuation with messed up indentation --{"key": ([], b"a" b"b" b"c")} -+{"key": ([], b"abc")} - - b"Unicode Escape sequence don't apply to bytes: \N{0x} \u{ABCD} \U{ABCDEFGH}" -``` - - ### Output 2 ``` indent-style = space @@ -394,9 +367,9 @@ String \"\"\" # String continuation -b"Let's" b'start' b'with' b'a' b'simple' b'example' +b"Let'sstartwithasimpleexample" -b"Let's" b'start' b'with' b'a' b'simple' b'example' b'now repeat after me:' b'I am confident' b'I am confident' b'I am confident' b'I am confident' b'I am confident' +b"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident" ( b"Let's" @@ -463,34 +436,7 @@ test_particular = [ ] # Parenthesized string continuation with messed up indentation -{'key': ([], b'a' b'b' b'c')} +{'key': ([], b'abc')} b"Unicode Escape sequence don't apply to bytes: \N{0x} \u{ABCD} \U{ABCDEFGH}" ``` - - -#### Preview changes -```diff ---- Stable -+++ Preview -@@ -63,9 +63,9 @@ - - # String continuation - --b"Let's" b'start' b'with' b'a' b'simple' b'example' -+b"Let'sstartwithasimpleexample" - --b"Let's" b'start' b'with' b'a' b'simple' b'example' b'now repeat after me:' b'I am confident' b'I am confident' b'I am confident' b'I am confident' b'I am confident' -+b"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident" - - ( - b"Let's" -@@ -132,6 +132,6 @@ - ] - - # Parenthesized string continuation with messed up indentation --{'key': ([], b'a' b'b' b'c')} -+{'key': ([], b'abc')} - - b"Unicode Escape sequence don't apply to bytes: \N{0x} \u{ABCD} \U{ABCDEFGH}" -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap index 94838c61ec..5a9fbef4b9 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap @@ -1510,7 +1510,7 @@ source_type = Python ``` ```python -(f"{one}" f"{two}") +(f"{one}{two}") rf"Not-so-tricky \"quote" @@ -1518,13 +1518,13 @@ rf"Not-so-tricky \"quote" # Regression test for fstrings dropping comments result_f = ( "Traceback (most recent call last):\n" - f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_f + 5}, in _check_recursive_traceback_display\n' " f()\n" - f' File "{__file__}", line {lineno_f+1}, in f\n' + f' File "{__file__}", line {lineno_f + 1}, in f\n' " f()\n" - f' File "{__file__}", line {lineno_f+1}, in f\n' + f' File "{__file__}", line {lineno_f + 1}, in f\n' " f()\n" - f' File "{__file__}", line {lineno_f+1}, in f\n' + f' File "{__file__}", line {lineno_f + 1}, in f\n' " f()\n" # XXX: The following line changes depending on whether the tests # are run through the interactive interpreter or with -m @@ -1550,7 +1550,7 @@ result_f = ( ) ( - f"{1}" f"{2}" # comment 3 + f"{1}{2}" # comment 3 ) ( @@ -1579,29 +1579,31 @@ z = f"""a{""}b""" f'''c{1}d"""e''' x = f"{a}" x = f"{ a = }" -x = f"{ # comment 6 - a }" +x = f"{ # comment 6 + a +}" x = f"{ # comment 7 a = }" # Remove the parentheses as adding them doesn't make then fit within the line length limit. # This is similar to how we format it before f-string formatting. -aaaaaaaaaaa = f"asaaaaaaaaaaaaaaaa { aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd } cccccccccc" +aaaaaaaaaaa = f"asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd} cccccccccc" # Here, we would use the best fit layout to put the f-string indented on the next line # similar to the next example. aaaaaaaaaaa = ( - f"asaaaaaaaaaaaaaaaa { aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc } cccccccccc" + f"asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc} cccccccccc" ) aaaaaaaaaaa = ( - f"asaaaaaaaaaaaaaaaa { aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc } cccccccccc" + f"asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc} cccccccccc" ) # This should never add the optional parentheses because even after adding them, the # f-string exceeds the line length limit. -x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ccccccccccccccc" +x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa {'bbbbbbbbbbbbbbbbbbbbbbbbbbbbb'} ccccccccccccccc" x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" = } ccccccccccccccc" -x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 8 - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ccccccccccccccc" +x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 8 + 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbb' +} ccccccccccccccc" x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 9 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" = } ccccccccccccccc" x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 9 @@ -1610,35 +1612,41 @@ x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 9 # Multiple larger expressions which exceeds the line length limit. Here, we need to decide # whether to split at the first or second expression. This should work similarly to the # assignment statement formatting where we split from right to left in preview mode. -x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" +x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc {ddddddddddddddd} eeeeeeeeeeeeee" # The above example won't split but when we start introducing line breaks: -x = f"aaaaaaaaaaaa { - bbbbbbbbbbbbbb } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" -x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb - } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" -x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb } cccccccccccccccccccc { - ddddddddddddddd } eeeeeeeeeeeeee" -x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb } cccccccccccccccccccc { ddddddddddddddd - } eeeeeeeeeeeeee" +x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { + ddddddddddddddd +} eeeeeeeeeeeeee" +x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { + ddddddddddddddd +} eeeeeeeeeeeeee" +x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { + ddddddddddddddd +} eeeeeeeeeeeeee" +x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { + ddddddddddddddd +} eeeeeeeeeeeeee" # But, in case comments are present, we would split at the expression containing the # comments: -x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb # comment 10 - } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" -x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb - } cccccccccccccccccccc { # comment 11 - ddddddddddddddd } eeeeeeeeeeeeee" +x = f"aaaaaaaaaaaa { + bbbbbbbbbbbbbb # comment 10 +} cccccccccccccccccccc {ddddddddddddddd} eeeeeeeeeeeeee" +x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { # comment 11 + ddddddddddddddd +} eeeeeeeeeeeeee" # Here, the expression part itself starts with a curly brace so we need to add an extra # space between the opening curly brace and the expression. x = f"{ {'x': 1, 'y': 2} }" # Although the extra space isn't required before the ending curly brace, we add it for # consistency. -x = f"{ {'x': 1, 'y': 2}}" +x = f"{ {'x': 1, 'y': 2} }" x = f"{ {'x': 1, 'y': 2} = }" x = f"{ # comment 12 - {'x': 1, 'y': 2} }" + {'x': 1, 'y': 2} +}" x = f"{ # comment 13 {'x': 1, 'y': 2} = }" # But, if there's a format specifier or a conversion flag then we don't need to add @@ -1648,12 +1656,16 @@ x = f"aaaaa { {'aaaaaa', 'bbbbbbb', 'ccccccccc'}:.3f} bbbbbb" # But, in this case, we would split the expression itself because it exceeds the line # length limit so we need not add the extra space. -xxxxxxx = f"{ - {'aaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbb', 'ccccccccccccccccccccc'} -}" +xxxxxxx = ( + f"{ {'aaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbb', 'ccccccccccccccccccccc'} }" +) # And, split the expression itself because it exceeds the line length. xxxxxxx = f"{ - {'aaaaaaaaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccccccccc'} + { + 'aaaaaaaaaaaaaaaaaaaaaaaaa', + 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', + 'cccccccccccccccccccccccccc', + } }" ############################################################################################# @@ -1663,7 +1675,7 @@ f"foo 'bar' {x}" f'foo "bar" {x}' f'foo "bar" {x}' f"foo 'bar' {x}" -f"foo {"bar"}" +f"foo {'bar'}" f"single quoted '{x}' double quoted \"{x}\"" # Same number of quotes => use preferred quote style f'single quote \' {x} double quoted "{x}"' # More double quotes => use single quotes @@ -1675,22 +1687,22 @@ rf"flip quotes {x}" # Use preferred quotes, because raw string contains now quo # Here, the formatter will remove the escapes which is correct because they aren't allowed # pre 3.12. This means we can assume that the f-string is used in the context of 3.12. -f"foo {'\'bar\''}" -f"foo {'\"bar\"'}" +f"foo {"'bar'"}" +f"foo {'"bar"'}" # Quotes inside the expressions have no impact on the quote selection of the outer string. # Required so that the following two examples result in the same formatting. -f'foo {10 + len("bar")}' +f"foo {10 + len('bar')}" f"foo {10 + len('bar')}" # Pre 312, preserve the outer quotes if the f-string contains quotes in the debug expression f'foo {10 + len("bar")=}' -f'''foo {10 + len('''bar''')=}''' +f"""foo {10 + len('''bar''')=}""" f"""foo {10 + len('bar')=}""" # Fine to change the quotes because it uses triple quotes # Triple-quoted strings # It's ok to use the same quote char for the inner string if it's single-quoted. -f"""test {'inner'}""" +f"""test {"inner"}""" f"""test {"inner"}""" # But if the inner string is also triple-quoted then we should preserve the existing quotes. f"""test {'''inner'''}""" @@ -1702,7 +1714,7 @@ f'{b"""other " """}' f'{f"""other " """}' # Not valid Pre 3.12 -f"""test {f'inner {'''inner inner'''}'}""" +f"""test {f"inner {'''inner inner'''}"}""" f"""test {f'''inner {"""inner inner"""}'''}""" # Magic trailing comma @@ -1714,63 +1726,66 @@ f"""test {f'''inner {"""inner inner"""}'''}""" f"aaaaaaa {['aaaaaaaaaaaaaaa', 'bbbbbbbbbbbbb', 'ccccccccccccccccc', 'ddddddddddddddd', 'eeeeeeeeeeeeee']} aaaaaaa" # And, if the trailing comma is already present, we still need to remove it. -f"aaaaaaa {['aaaaaaaaaaaaaaa', 'bbbbbbbbbbbbb', 'ccccccccccccccccc', 'ddddddddddddddd', 'eeeeeeeeeeeeee',]} aaaaaaa" +f"aaaaaaa {['aaaaaaaaaaaaaaa', 'bbbbbbbbbbbbb', 'ccccccccccccccccc', 'ddddddddddddddd', 'eeeeeeeeeeeeee']} aaaaaaa" # Keep this Multiline by breaking it at the square brackets. -f"""aaaaaa {[ - xxxxxxxx, - yyyyyyyy, -]} ccc""" +f"""aaaaaa { + [ + xxxxxxxx, + yyyyyyyy, + ] +} ccc""" # Add the magic trailing comma because the elements don't fit within the line length limit # when collapsed. -f"aaaaaa {[ - xxxxxxxxxxxx, - xxxxxxxxxxxx, - xxxxxxxxxxxx, - xxxxxxxxxxxx, - xxxxxxxxxxxx, - xxxxxxxxxxxx, - yyyyyyyyyyyy -]} ccccccc" +f"aaaaaa { + [ + xxxxxxxxxxxx, + xxxxxxxxxxxx, + xxxxxxxxxxxx, + xxxxxxxxxxxx, + xxxxxxxxxxxx, + xxxxxxxxxxxx, + yyyyyyyyyyyy, + ] +} ccccccc" # Remove the parentheses because they aren't required xxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbb { - xxxxxxxxxxx # comment 14 - + yyyyyyyyyy - } dddddddddd" + xxxxxxxxxxx # comment 14 + + yyyyyyyyyy +} dddddddddd" # Comments # No comments should be dropped! -f"{ # comment 15 +f"{ # comment 15 # comment 16 - foo # comment 17 + foo # comment 17 # comment 18 }" # comment 19 # comment 20 # Single-quoted f-strings with a format specificer can be multiline f"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 # trailing newline -f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc { - variable:.3f} ddddddddddddddd eeeeeeee""" +f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f} ddddddddddddddd eeeeeeee""" # But, we can break the ones which don't have a format specifier -f"""fooooooooooooooooooo barrrrrrrrrrrrrrrrrrr { - xxxxxxxxxxxxxxx:.3f} aaaaaaaaaaaaaaaaa { xxxxxxxxxxxxxxxxxxxx } bbbbbbbbbbbb""" +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 # which is part of the format specifier -aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa { - aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f +aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f # comment } cccccccccc""" -aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa { - aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f +aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f # comment} cccccccccc""" # Conversion flags @@ -1778,24 +1793,21 @@ aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa { # This is not a valid Python code because of the additional whitespace between the `!` # and conversion type. But, our parser isn't strict about this. This should probably be # removed once we have a strict parser. -x = f"aaaaaaaaa { x ! r }" +x = f"aaaaaaaaa {x!r}" # Even in the case of debug expressions, we only need to preserve the whitespace within # the expression part of the replacement field. -x = f"aaaaaaaaa { x = ! r }" +x = f"aaaaaaaaa { x = !r}" # Combine conversion flags with format specifiers -x = f"{x = ! s - :>0 - - }" +x = f"{x = !s:>0}" # This is interesting. There can be a comment after the format specifier but only if it's # on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details. # We'll format is as trailing comments. -x = f"{x !s - :>0 - # comment 21 - }" +x = f"{ + x!s:>0 + # comment 21 +}" x = f""" { # comment 22 @@ -1804,19 +1816,19 @@ x = f""" # Here, the debug expression is in a nested f-string so we should start preserving # whitespaces from that point onwards. This means we should format the outer f-string. -x = f"""{"foo " + # comment 24 - f"{ x = +x = f"""{ + "foo " # comment 24 + + f"{ x = - }" # comment 25 - } + }" # comment 25 +} """ # Mix of various features. f"{ # comment 26 - foo # after foo - :>{ - x # after x - } + foo:>{ # after foo + x # after x + } # comment 27 # comment 28 } woah {x}" @@ -1826,29 +1838,36 @@ f"{ # comment 26 # Even though this f-string has multiline expression, thus allowing us to break it at the # curly braces, the f-string fits on a single line if it's moved inside the parentheses. # We should prefer doing that instead. -aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ - expression}moreeeeeeeeeeeeeeeee" +aaaaaaaaaaaaaaaaaa = ( + f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeee" +) # Same as above -xxxxxxx = f"{ - {'aaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccccccccc'} -}" +xxxxxxx = ( + f"{ {'aaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccccccccc'} }" +) # Similar to the previous example, but the f-string will exceed the line length limit, # we shouldn't add any parentheses here. xxxxxxx = f"{ - {'aaaaaaaaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccccccccc'} + { + 'aaaaaaaaaaaaaaaaaaaaaaaaa', + 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', + 'cccccccccccccccccccccccccc', + } }" # Same as above but with an inline comment. The f-string should be formatted inside the # parentheses and the comment should be part of the line inside the parentheses. -aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ - expression}moreeeeeeeeeeeeeeeee" # comment +aaaaaaaaaaaaaaaaaa = ( + f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeee" # comment +) # Similar to the previous example but this time parenthesizing doesn't work because it # exceeds the line length. So, avoid parenthesizing this f-string. aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ - expression}moreeeeeeeeeeeeeeeee" # comment loooooooong + expression +}moreeeeeeeeeeeeeeeee" # comment loooooooong # Similar to the previous example but we start with the parenthesized layout. This should # remove the parentheses and format the f-string on a single line. This shows that the @@ -1860,17 +1879,24 @@ aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeee # The following f-strings are going to break because of the trailing comma so we should # avoid using the best fit layout and instead use the default layout. # left-to-right -aaaa = f"aaaa {[ - 1, 2, -]} bbbb" +aaaa = f"aaaa { + [ + 1, + 2, + ] +} bbbb" # right-to-left -aaaa, bbbb = f"aaaa {[ - 1, 2, -]} bbbb" +aaaa, bbbb = f"aaaa { + [ + 1, + 2, + ] +} bbbb" # Using the right-to-left assignment statement variant. -aaaaaaaaaaaaaaaaaa, bbbbbbbbbbb = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ - expression}moreeeeeeeeeeeeeeeee" # comment +aaaaaaaaaaaaaaaaaa, bbbbbbbbbbb = ( + f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeee" # comment +) # Here, the f-string layout is flat but it exceeds the line length limit. This shouldn't # try the custom best fit layout because the f-string doesn't have any split points. @@ -1885,60 +1911,66 @@ aaaaaaaaaaaa["bbbbbbbbbbbbbbbb"] = ( # But, the following f-string does have a split point because of the multiline expression. aaaaaaaaaaaa["bbbbbbbbbbbbbbbb"] = f"aaaaaaaaaaaaaaaaaaa { - aaaaaaaaa + bbbbbbbbbbb + cccccccccccccc} ddddddddddddddddddd" + aaaaaaaaa + bbbbbbbbbbb + cccccccccccccc +} ddddddddddddddddddd" aaaaaaaaaaaa["bbbbbbbbbbbbbbbb"] = f"aaaaaaaaaaaaaaaaaaa { - aaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccc + dddddddddddddddddddddddddddd} ddddddddddddddddddd" + aaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbb + + cccccccccccccccccccccc + + dddddddddddddddddddddddddddd +} ddddddddddddddddddd" # This is an implicitly concatenated f-string but it cannot be joined because otherwise # it'll exceed the line length limit. So, the two f-strings will be inside parentheses # instead and the inline comment should be outside the parentheses. a = ( - f"test{ - expression -}flat" - f"can be { - joined -} togethereeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + f"test{expression}flat" + f"can be {joined} togethereeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" ) # inline # Similar to the above example but this fits within the line length limit. -a = ( - f"test{ - expression -}flat" - f"can be { - joined -} togethereeeeeeeeeeeeeeeeeeeeeeeeeee" -) # inline +a = f"test{expression}flatcan be {joined} togethereeeeeeeeeeeeeeeeeeeeeeeeeee" # inline # The following test cases are adopted from implicit string concatenation but for a # single f-string instead. # Don't inline f-strings that contain expressions that are guaranteed to split, e.g. because of a magic trailing comma aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -[a,] + [ + a, + ] }moreeeeeeeeeeeeeeeeeeee" # comment aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -[a,] + [ + a, + ] }moreeeeeeeeeeeeeeeeeeee" # comment aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -[a,] + [ + a, + ] }moreeeeeeeeeeeeeeeeeeee" # comment aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -[a,] + [ + a, + ] }moreeeeeeeeeeeeeeeeeeee" # comment # Don't inline f-strings that contain commented expressions -aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{[ +aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ + [ a # comment - ]}moreeeeeeeeeeeeeeeeeetest" # comment + ] +}moreeeeeeeeeeeeeeeeeetest" # comment -aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeee{[ +aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ + [ a # comment - ]}moreeeeeeeeeeeeeeeeeetest" # comment + ] +}moreeeeeeeeeeeeeeeeeetest" # comment # Don't inline f-strings with multiline debug expressions or format specifiers aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ @@ -1957,31 +1989,37 @@ aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeee{a =}moreeeeeeeeeeeeeeeeeetest" # comment # This is not a multiline f-string even though it has a newline after the format specifier. -aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ - a:.3f - }moreeeeeeeeeeeeeeeeeetest" # comment +aaaaaaaaaaaaaaaaaa = ( + f"testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment +) -aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ - a:.3f - }moreeeeeeeeeeeeeeeeeetest" # comment +aaaaaaaaaaaaaaaaaa = ( + f"testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment +) # 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 -aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{ - a:.3f +aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f }moreeeeeeeeeeeeeeeeeetest""" # comment # Remove the parentheses here -aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{[a, b, - # comment - ]}moee" # comment +aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ + [ + a, + b, + # comment + ] +}moee" # comment # ... but not here because of the ownline comment aaaaaaaaaaaaaaaaaa = ( - f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{[a, b, - ]}moee" + f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ + [ + a, + b, + ] + }moee" # comment ) @@ -1993,13 +2031,11 @@ if f"aaaaaaaaaaa {ttttteeeeeeeeest} more { pass if f"aaaaaaaaaaa {ttttteeeeeeeeest} more { - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - }": + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +}": pass -if f"aaaaaaaaaaa {ttttteeeeeeeeest} more { - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -}": +if f"aaaaaaaaaaa {ttttteeeeeeeeest} more {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}": pass if f"aaaaaaaaaaa {ttttteeeeeeeeest} more { # comment @@ -2007,24 +2043,33 @@ if f"aaaaaaaaaaa {ttttteeeeeeeeest} more { # comment }": pass -if f"aaaaaaaaaaa {[ttttteeeeeeeeest,]} more { - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +if f"aaaaaaaaaaa { + [ + ttttteeeeeeeeest, + ] +} more {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}": + pass + +if f"aaaaaaaaaaa { + [ + ttttteeeeeeeeest, + ] +} more { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa }": pass -if f"aaaaaaaaaaa {[ttttteeeeeeeeest,]} more { - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - }": - pass - -if f"aaaaaaaaaaa {[ttttteeeeeeeeest,]} more { +if f"aaaaaaaaaaa { + [ + ttttteeeeeeeeest, + ] +} more { aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa }": pass # For loops -for a in f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ - expression}moreeeeeeeeeeeeeeeeeeee": +for a in f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeeeeee": pass for a in ( @@ -2033,7 +2078,8 @@ for a in ( pass for a in f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ - expression}moreeeeeeeeeeeeeeeeeeeeeeeeeeeeee": + expression +}moreeeeeeeeeeeeeeeeeeeeeeeeeeeeee": pass for a in ( @@ -2042,8 +2088,7 @@ for a in ( pass # With statements -with f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ - expression}moreeeeeeeeeeeeeeeeeeee": +with f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeeeeee": pass with ( @@ -2052,7 +2097,8 @@ with ( pass with f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ - expression}moreeeeeeeeeeeeeeeeeeeeeeeeeeeeee": + expression +}moreeeeeeeeeeeeeeeeeeeeeeeeeeeeee": pass with ( @@ -2061,78 +2107,80 @@ with ( pass # Assert statements -assert f"aaaaaaaaa{ - expression}bbbbbbbbbbbb", f"cccccccccc{ - expression}dddddddddd" +assert f"aaaaaaaaa{expression}bbbbbbbbbbbb", f"cccccccccc{expression}dddddddddd" assert f"aaaaaaaaa{expression}bbbbbbbbbbbb", f"cccccccccccccccc{ - expression}dddddddddddddddd" + expression +}dddddddddddddddd" -assert ( - f"aaaaaaaaa{expression}bbbbbbbbbbbb" -), f"cccccccccccccccc{expression}dddddddddddddddd" +assert f"aaaaaaaaa{expression}bbbbbbbbbbbb", ( + f"cccccccccccccccc{expression}dddddddddddddddd" +) assert f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{ - expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", ( - f"ccccccc{expression}dddddddddd" -) + expression +}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", f"ccccccc{expression}dddddddddd" assert ( f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" ), f"ccccccc{expression}dddddddddd" assert f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{ - expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", f"ccccccccccccccccccccc { - expression} dddddddddddddddddddddddddd" + expression +}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", f"ccccccccccccccccccccc { + expression +} dddddddddddddddddddddddddd" -assert f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", f"cccccccccccccccccccccccccccccccc {expression} ddddddddddddddddddddddddddddddddddddd" - -# F-strings as a single argument to a call expression to test whether it's huggable or not. -call( - f"{ - testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -}" +assert ( + f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" +), ( + f"cccccccccccccccccccccccccccccccc {expression} ddddddddddddddddddddddddddddddddddddd" ) +# F-strings as a single argument to a call expression to test whether it's huggable or not. +call(f"{testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee}") + call( - f"{ - testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -}" + f"{testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee}" ) call( f"{ # comment - testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -}" + testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + }" ) call( f"""aaaaaaaaaaaaaaaa bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee}""" ) -call(f"""aaaaaaaaaaaaaaaa bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - }""") +call( + f"""aaaaaaaaaaaaaaaa bbbbbbbbbb { + testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + }""" +) call(f"""aaaaaaaaaaaaaaaa - bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - }""") + bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee}""") call(f"""aaaaaaaaaaaaaaaa - bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee # comment - }""") + bbbbbbbbbb { + testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee # comment +}""") call( f"""aaaaaaaaaaaaaaaa - bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee # comment - }""" + bbbbbbbbbb { + testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee # comment + }""" ) call( f"{ - aaaaaa - + '''test + aaaaaa + + '''test more''' -}" + }" ) # Indentation @@ -2144,37 +2192,37 @@ if indent0: if indent2: foo = f"""hello world hello { - f"aaaaaaa { - [ - 'aaaaaaaaaaaaaaaaaaaaa', - 'bbbbbbbbbbbbbbbbbbbbb', - 'ccccccccccccccccccccc', - 'ddddddddddddddddddddd' - ] - } bbbbbbbb" + - [ - 'aaaaaaaaaaaaaaaaaaaaa', - 'bbbbbbbbbbbbbbbbbbbbb', - 'ccccccccccccccccccccc', - 'ddddddddddddddddddddd' - ] - } -------- + f"aaaaaaa { + [ + 'aaaaaaaaaaaaaaaaaaaaa', + 'bbbbbbbbbbbbbbbbbbbbb', + 'ccccccccccccccccccccc', + 'ddddddddddddddddddddd', + ] + } bbbbbbbb" + + [ + "aaaaaaaaaaaaaaaaaaaaa", + "bbbbbbbbbbbbbbbbbbbbb", + "ccccccccccccccccccccc", + "ddddddddddddddddddddd", + ] + } -------- """ # Implicit concatenated f-string containing quotes _ = ( - 'This string should change its quotes to double quotes' - f'This string uses double quotes in an expression {"it's a quote"}' - f'This f-string does not use any quotes.' + "This string should change its quotes to double quotes" + f"This string uses double quotes in an expression {"it's a quote"}" + f"This f-string does not use any quotes." ) # Regression test for https://github.com/astral-sh/ruff/issues/14487 f"aaaaaaaaaaaaaaaaaaaaaaaaaa {10**27} bbbbbbbbbbbbbbbbbbbbbbbbbb ccccccccccccccccccccccccc" # Regression test for https://github.com/astral-sh/ruff/issues/14778 -f"{'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'a' if True else ""}" -f"{'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'a' if True else ""}" +f"{'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' if True else ''}" +f"{'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' if True else ''}" # Quotes reuse f"{'a'}" @@ -2194,18 +2242,18 @@ f'{f"""other " """}' f'{1: hy "user"}' f'{1:hy "user"}' f'{1: abcd "{1}" }' -f'{1: abcd "{'aa'}" }' +f'{1: abcd "{"aa"}" }' f'{1=: "abcd {'aa'}}' -f'{x:a{z:hy "user"}} \'\'\'' +f"{x:a{z:hy \"user\"}} '''" # Changing the outer quotes is fine because the format-spec is in a nested expression. -f'{f'{z=:hy "user"}'} \'\'\'' +f"{f'{z=:hy "user"}'} '''" # We have to be careful about changing the quotes if the f-string has a debug expression because it is inserted verbatim. f'{1=: "abcd \'\'}' # Don't change the outer quotes, or it results in a syntax error -f'{1=: abcd \'\'}' # Changing the quotes here is fine because the inner quotes aren't the opposite quotes -f'{1=: abcd \"\"}' # Changing the quotes here is fine because the inner quotes are escaped +f"{1=: abcd \'\'}" # Changing the quotes here is fine because the inner quotes aren't the opposite quotes +f"{1=: abcd \"\"}" # Changing the quotes here is fine because the inner quotes are escaped # Don't change the quotes in the following cases: f'{x=:hy "user"} \'\'\'' f'{x=:a{y:hy "user"}} \'\'\'' @@ -2218,874 +2266,3 @@ f"""{1=: "this" is fine}""" f"""{1=: "this" is fine}""" # Change quotes to double quotes because they're preferred f'{1=: {'ab"cd"'}}' # It's okay if the quotes are in an expression part. ``` - - -#### Preview changes -```diff ---- Stable -+++ Preview -@@ -1,4 +1,4 @@ --(f"{one}" f"{two}") -+(f"{one}{two}") - - - rf"Not-so-tricky \"quote" -@@ -6,13 +6,13 @@ - # Regression test for fstrings dropping comments - result_f = ( - "Traceback (most recent call last):\n" -- f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' -+ f' File "{__file__}", line {lineno_f + 5}, in _check_recursive_traceback_display\n' - " f()\n" -- f' File "{__file__}", line {lineno_f+1}, in f\n' -+ f' File "{__file__}", line {lineno_f + 1}, in f\n' - " f()\n" -- f' File "{__file__}", line {lineno_f+1}, in f\n' -+ f' File "{__file__}", line {lineno_f + 1}, in f\n' - " f()\n" -- f' File "{__file__}", line {lineno_f+1}, in f\n' -+ f' File "{__file__}", line {lineno_f + 1}, in f\n' - " f()\n" - # XXX: The following line changes depending on whether the tests - # are run through the interactive interpreter or with -m -@@ -38,7 +38,7 @@ - ) - - ( -- f"{1}" f"{2}" # comment 3 -+ f"{1}{2}" # comment 3 - ) - - ( -@@ -67,29 +67,31 @@ - x = f"{a}" - x = f"{ - a = }" --x = f"{ # comment 6 -- a }" -+x = f"{ # comment 6 -+ a -+}" - x = f"{ # comment 7 - a = }" - - # Remove the parentheses as adding them doesn't make then fit within the line length limit. - # This is similar to how we format it before f-string formatting. --aaaaaaaaaaa = f"asaaaaaaaaaaaaaaaa { aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd } cccccccccc" -+aaaaaaaaaaa = f"asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd} cccccccccc" - # Here, we would use the best fit layout to put the f-string indented on the next line - # similar to the next example. - aaaaaaaaaaa = ( -- f"asaaaaaaaaaaaaaaaa { aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc } cccccccccc" -+ f"asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc} cccccccccc" - ) - aaaaaaaaaaa = ( -- f"asaaaaaaaaaaaaaaaa { aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc } cccccccccc" -+ f"asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc} cccccccccc" - ) - - # This should never add the optional parentheses because even after adding them, the - # f-string exceeds the line length limit. --x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ccccccccccccccc" -+x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa {'bbbbbbbbbbbbbbbbbbbbbbbbbbbbb'} ccccccccccccccc" - x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" = } ccccccccccccccc" --x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 8 -- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ccccccccccccccc" -+x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 8 -+ 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbb' -+} ccccccccccccccc" - x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 9 - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" = } ccccccccccccccc" - x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 9 -@@ -98,35 +100,41 @@ - # Multiple larger expressions which exceeds the line length limit. Here, we need to decide - # whether to split at the first or second expression. This should work similarly to the - # assignment statement formatting where we split from right to left in preview mode. --x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" -+x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc {ddddddddddddddd} eeeeeeeeeeeeee" - - # The above example won't split but when we start introducing line breaks: --x = f"aaaaaaaaaaaa { -- bbbbbbbbbbbbbb } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" --x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb -- } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" --x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb } cccccccccccccccccccc { -- ddddddddddddddd } eeeeeeeeeeeeee" --x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb } cccccccccccccccccccc { ddddddddddddddd -- } eeeeeeeeeeeeee" -+x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { -+ ddddddddddddddd -+} eeeeeeeeeeeeee" -+x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { -+ ddddddddddddddd -+} eeeeeeeeeeeeee" -+x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { -+ ddddddddddddddd -+} eeeeeeeeeeeeee" -+x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { -+ ddddddddddddddd -+} eeeeeeeeeeeeee" - - # But, in case comments are present, we would split at the expression containing the - # comments: --x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb # comment 10 -- } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" --x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb -- } cccccccccccccccccccc { # comment 11 -- ddddddddddddddd } eeeeeeeeeeeeee" -+x = f"aaaaaaaaaaaa { -+ bbbbbbbbbbbbbb # comment 10 -+} cccccccccccccccccccc {ddddddddddddddd} eeeeeeeeeeeeee" -+x = f"aaaaaaaaaaaa {bbbbbbbbbbbbbb} cccccccccccccccccccc { # comment 11 -+ ddddddddddddddd -+} eeeeeeeeeeeeee" - - # Here, the expression part itself starts with a curly brace so we need to add an extra - # space between the opening curly brace and the expression. - x = f"{ {'x': 1, 'y': 2} }" - # Although the extra space isn't required before the ending curly brace, we add it for - # consistency. --x = f"{ {'x': 1, 'y': 2}}" -+x = f"{ {'x': 1, 'y': 2} }" - x = f"{ {'x': 1, 'y': 2} = }" - x = f"{ # comment 12 -- {'x': 1, 'y': 2} }" -+ {'x': 1, 'y': 2} -+}" - x = f"{ # comment 13 - {'x': 1, 'y': 2} = }" - # But, if there's a format specifier or a conversion flag then we don't need to add -@@ -136,12 +144,16 @@ - - # But, in this case, we would split the expression itself because it exceeds the line - # length limit so we need not add the extra space. --xxxxxxx = f"{ -- {'aaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbb', 'ccccccccccccccccccccc'} --}" -+xxxxxxx = ( -+ f"{ {'aaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbb', 'ccccccccccccccccccccc'} }" -+) - # And, split the expression itself because it exceeds the line length. - xxxxxxx = f"{ -- {'aaaaaaaaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccccccccc'} -+ { -+ 'aaaaaaaaaaaaaaaaaaaaaaaaa', -+ 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', -+ 'cccccccccccccccccccccccccc', -+ } - }" - - ############################################################################################# -@@ -151,7 +163,7 @@ - f'foo "bar" {x}' - f'foo "bar" {x}' - f"foo 'bar' {x}" --f"foo {"bar"}" -+f"foo {'bar'}" - - f"single quoted '{x}' double quoted \"{x}\"" # Same number of quotes => use preferred quote style - f'single quote \' {x} double quoted "{x}"' # More double quotes => use single quotes -@@ -163,23 +175,23 @@ - - # Here, the formatter will remove the escapes which is correct because they aren't allowed - # pre 3.12. This means we can assume that the f-string is used in the context of 3.12. --f"foo {'\'bar\''}" --f"foo {'\"bar\"'}" -+f"foo {"'bar'"}" -+f"foo {'"bar"'}" - - # Quotes inside the expressions have no impact on the quote selection of the outer string. - # Required so that the following two examples result in the same formatting. --f'foo {10 + len("bar")}' - f"foo {10 + len('bar')}" -+f"foo {10 + len('bar')}" - - # Pre 312, preserve the outer quotes if the f-string contains quotes in the debug expression - f'foo {10 + len("bar")=}' --f'''foo {10 + len('''bar''')=}''' -+f"""foo {10 + len('''bar''')=}""" - f"""foo {10 + len('bar')=}""" # Fine to change the quotes because it uses triple quotes - - # Triple-quoted strings - # It's ok to use the same quote char for the inner string if it's single-quoted. --f"""test {'inner'}""" - f"""test {"inner"}""" -+f"""test {"inner"}""" - # But if the inner string is also triple-quoted then we should preserve the existing quotes. - f"""test {'''inner'''}""" - -@@ -190,7 +202,7 @@ - f'{f"""other " """}' - - # Not valid Pre 3.12 --f"""test {f'inner {'''inner inner'''}'}""" -+f"""test {f"inner {'''inner inner'''}"}""" - f"""test {f'''inner {"""inner inner"""}'''}""" - - # Magic trailing comma -@@ -202,63 +214,66 @@ - f"aaaaaaa {['aaaaaaaaaaaaaaa', 'bbbbbbbbbbbbb', 'ccccccccccccccccc', 'ddddddddddddddd', 'eeeeeeeeeeeeee']} aaaaaaa" - - # And, if the trailing comma is already present, we still need to remove it. --f"aaaaaaa {['aaaaaaaaaaaaaaa', 'bbbbbbbbbbbbb', 'ccccccccccccccccc', 'ddddddddddddddd', 'eeeeeeeeeeeeee',]} aaaaaaa" -+f"aaaaaaa {['aaaaaaaaaaaaaaa', 'bbbbbbbbbbbbb', 'ccccccccccccccccc', 'ddddddddddddddd', 'eeeeeeeeeeeeee']} aaaaaaa" - - # Keep this Multiline by breaking it at the square brackets. --f"""aaaaaa {[ -- xxxxxxxx, -- yyyyyyyy, --]} ccc""" -+f"""aaaaaa { -+ [ -+ xxxxxxxx, -+ yyyyyyyy, -+ ] -+} ccc""" - - # Add the magic trailing comma because the elements don't fit within the line length limit - # when collapsed. --f"aaaaaa {[ -- xxxxxxxxxxxx, -- xxxxxxxxxxxx, -- xxxxxxxxxxxx, -- xxxxxxxxxxxx, -- xxxxxxxxxxxx, -- xxxxxxxxxxxx, -- yyyyyyyyyyyy --]} ccccccc" -+f"aaaaaa { -+ [ -+ xxxxxxxxxxxx, -+ xxxxxxxxxxxx, -+ xxxxxxxxxxxx, -+ xxxxxxxxxxxx, -+ xxxxxxxxxxxx, -+ xxxxxxxxxxxx, -+ yyyyyyyyyyyy, -+ ] -+} ccccccc" - - # Remove the parentheses because they aren't required - xxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbb { -- xxxxxxxxxxx # comment 14 -- + yyyyyyyyyy -- } dddddddddd" -+ xxxxxxxxxxx # comment 14 -+ + yyyyyyyyyy -+} dddddddddd" - - # Comments - - # No comments should be dropped! --f"{ # comment 15 -+f"{ # comment 15 - # comment 16 -- foo # comment 17 -+ foo # comment 17 - # comment 18 - }" # comment 19 - # comment 20 - - # Single-quoted f-strings with a format specificer can be multiline - f"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 - # trailing newline --f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc { -- variable:.3f} ddddddddddddddd eeeeeeee""" -+f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f} ddddddddddddddd eeeeeeee""" - - # But, we can break the ones which don't have a format specifier --f"""fooooooooooooooooooo barrrrrrrrrrrrrrrrrrr { -- xxxxxxxxxxxxxxx:.3f} aaaaaaaaaaaaaaaaa { xxxxxxxxxxxxxxxxxxxx } bbbbbbbbbbbb""" -+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 - # which is part of the format specifier --aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa { -- aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f -+aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f - # comment - } cccccccccc""" --aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa { -- aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f -+aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd:.3f - # comment} cccccccccc""" - - # Conversion flags -@@ -266,24 +281,21 @@ - # This is not a valid Python code because of the additional whitespace between the `!` - # and conversion type. But, our parser isn't strict about this. This should probably be - # removed once we have a strict parser. --x = f"aaaaaaaaa { x ! r }" -+x = f"aaaaaaaaa {x!r}" - - # Even in the case of debug expressions, we only need to preserve the whitespace within - # the expression part of the replacement field. --x = f"aaaaaaaaa { x = ! r }" -+x = f"aaaaaaaaa { x = !r}" - - # Combine conversion flags with format specifiers --x = f"{x = ! s -- :>0 -- -- }" -+x = f"{x = !s:>0}" - # This is interesting. There can be a comment after the format specifier but only if it's - # on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details. - # We'll format is as trailing comments. --x = f"{x !s -- :>0 -- # comment 21 -- }" -+x = f"{ -+ x!s:>0 -+ # comment 21 -+}" - - x = f""" - { # comment 22 -@@ -292,19 +304,19 @@ - - # Here, the debug expression is in a nested f-string so we should start preserving - # whitespaces from that point onwards. This means we should format the outer f-string. --x = f"""{"foo " + # comment 24 -- f"{ x = -+x = f"""{ -+ "foo " # comment 24 -+ + f"{ x = - -- }" # comment 25 -- } -+ }" # comment 25 -+} - """ - - # Mix of various features. - f"{ # comment 26 -- foo # after foo -- :>{ -- x # after x -- } -+ foo:>{ # after foo -+ x # after x -+ } - # comment 27 - # comment 28 - } woah {x}" -@@ -314,29 +326,36 @@ - # Even though this f-string has multiline expression, thus allowing us to break it at the - # curly braces, the f-string fits on a single line if it's moved inside the parentheses. - # We should prefer doing that instead. --aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -- expression}moreeeeeeeeeeeeeeeee" -+aaaaaaaaaaaaaaaaaa = ( -+ f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeee" -+) - - # Same as above --xxxxxxx = f"{ -- {'aaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccccccccc'} --}" -+xxxxxxx = ( -+ f"{ {'aaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccccccccc'} }" -+) - - # Similar to the previous example, but the f-string will exceed the line length limit, - # we shouldn't add any parentheses here. - xxxxxxx = f"{ -- {'aaaaaaaaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccccccccc'} -+ { -+ 'aaaaaaaaaaaaaaaaaaaaaaaaa', -+ 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', -+ 'cccccccccccccccccccccccccc', -+ } - }" - - # Same as above but with an inline comment. The f-string should be formatted inside the - # parentheses and the comment should be part of the line inside the parentheses. --aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -- expression}moreeeeeeeeeeeeeeeee" # comment -+aaaaaaaaaaaaaaaaaa = ( -+ f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeee" # comment -+) - - # Similar to the previous example but this time parenthesizing doesn't work because it - # exceeds the line length. So, avoid parenthesizing this f-string. - aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -- expression}moreeeeeeeeeeeeeeeee" # comment loooooooong -+ expression -+}moreeeeeeeeeeeeeeeee" # comment loooooooong - - # Similar to the previous example but we start with the parenthesized layout. This should - # remove the parentheses and format the f-string on a single line. This shows that the -@@ -348,17 +367,24 @@ - # The following f-strings are going to break because of the trailing comma so we should - # avoid using the best fit layout and instead use the default layout. - # left-to-right --aaaa = f"aaaa {[ -- 1, 2, --]} bbbb" -+aaaa = f"aaaa { -+ [ -+ 1, -+ 2, -+ ] -+} bbbb" - # right-to-left --aaaa, bbbb = f"aaaa {[ -- 1, 2, --]} bbbb" -+aaaa, bbbb = f"aaaa { -+ [ -+ 1, -+ 2, -+ ] -+} bbbb" - - # Using the right-to-left assignment statement variant. --aaaaaaaaaaaaaaaaaa, bbbbbbbbbbb = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -- expression}moreeeeeeeeeeeeeeeee" # comment -+aaaaaaaaaaaaaaaaaa, bbbbbbbbbbb = ( -+ f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeee" # comment -+) - - # Here, the f-string layout is flat but it exceeds the line length limit. This shouldn't - # try the custom best fit layout because the f-string doesn't have any split points. -@@ -373,60 +399,66 @@ - - # But, the following f-string does have a split point because of the multiline expression. - aaaaaaaaaaaa["bbbbbbbbbbbbbbbb"] = f"aaaaaaaaaaaaaaaaaaa { -- aaaaaaaaa + bbbbbbbbbbb + cccccccccccccc} ddddddddddddddddddd" -+ aaaaaaaaa + bbbbbbbbbbb + cccccccccccccc -+} ddddddddddddddddddd" - aaaaaaaaaaaa["bbbbbbbbbbbbbbbb"] = f"aaaaaaaaaaaaaaaaaaa { -- aaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccc + dddddddddddddddddddddddddddd} ddddddddddddddddddd" -+ aaaaaaaaaaaaaaaaaaaa -+ + bbbbbbbbbbbbbbbbbbbbb -+ + cccccccccccccccccccccc -+ + dddddddddddddddddddddddddddd -+} ddddddddddddddddddd" - - # This is an implicitly concatenated f-string but it cannot be joined because otherwise - # it'll exceed the line length limit. So, the two f-strings will be inside parentheses - # instead and the inline comment should be outside the parentheses. - a = ( -- f"test{ -- expression --}flat" -- f"can be { -- joined --} togethereeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" -+ f"test{expression}flat" -+ f"can be {joined} togethereeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - ) # inline - - # Similar to the above example but this fits within the line length limit. --a = ( -- f"test{ -- expression --}flat" -- f"can be { -- joined --} togethereeeeeeeeeeeeeeeeeeeeeeeeeee" --) # inline -+a = f"test{expression}flatcan be {joined} togethereeeeeeeeeeeeeeeeeeeeeeeeeee" # inline - - # The following test cases are adopted from implicit string concatenation but for a - # single f-string instead. - - # Don't inline f-strings that contain expressions that are guaranteed to split, e.g. because of a magic trailing comma - aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ --[a,] -+ [ -+ a, -+ ] - }moreeeeeeeeeeeeeeeeeeee" # comment - - aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ --[a,] -+ [ -+ a, -+ ] - }moreeeeeeeeeeeeeeeeeeee" # comment - - aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ --[a,] -+ [ -+ a, -+ ] - }moreeeeeeeeeeeeeeeeeeee" # comment - - aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ --[a,] -+ [ -+ a, -+ ] - }moreeeeeeeeeeeeeeeeeeee" # comment - - # Don't inline f-strings that contain commented expressions --aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{[ -+aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ -+ [ - a # comment -- ]}moreeeeeeeeeeeeeeeeeetest" # comment -+ ] -+}moreeeeeeeeeeeeeeeeeetest" # comment - --aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeee{[ -+aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ -+ [ - a # comment -- ]}moreeeeeeeeeeeeeeeeeetest" # comment -+ ] -+}moreeeeeeeeeeeeeeeeeetest" # comment - - # Don't inline f-strings with multiline debug expressions or format specifiers - aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ -@@ -445,31 +477,37 @@ - =}moreeeeeeeeeeeeeeeeeetest" # comment - - # This is not a multiline f-string even though it has a newline after the format specifier. --aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ -- a:.3f -- }moreeeeeeeeeeeeeeeeeetest" # comment -+aaaaaaaaaaaaaaaaaa = ( -+ f"testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment -+) - --aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{ -- a:.3f -- }moreeeeeeeeeeeeeeeeeetest" # comment -+aaaaaaaaaaaaaaaaaa = ( -+ f"testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment -+) - - # 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 - --aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{ -- a:.3f -+aaaaaaaaaaaaaaaaaa = f"""testeeeeeeeeeeeeeeeeeeeeeeeee{a:.3f - }moreeeeeeeeeeeeeeeeeetest""" # comment - - # Remove the parentheses here --aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{[a, b, -- # comment -- ]}moee" # comment -+aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -+ [ -+ a, -+ b, -+ # comment -+ ] -+}moee" # comment - # ... but not here because of the ownline comment - aaaaaaaaaaaaaaaaaa = ( -- f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{[a, b, -- ]}moee" -+ f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -+ [ -+ a, -+ b, -+ ] -+ }moee" - # comment - ) - -@@ -481,13 +519,11 @@ - pass - - if f"aaaaaaaaaaa {ttttteeeeeeeeest} more { -- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -- }": -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -+}": - pass - --if f"aaaaaaaaaaa {ttttteeeeeeeeest} more { -- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --}": -+if f"aaaaaaaaaaa {ttttteeeeeeeeest} more {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}": - pass - - if f"aaaaaaaaaaa {ttttteeeeeeeeest} more { # comment -@@ -495,24 +531,33 @@ - }": - pass - --if f"aaaaaaaaaaa {[ttttteeeeeeeeest,]} more { -- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --}": -+if f"aaaaaaaaaaa { -+ [ -+ ttttteeeeeeeeest, -+ ] -+} more {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}": - pass - --if f"aaaaaaaaaaa {[ttttteeeeeeeeest,]} more { -- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -- }": -+if f"aaaaaaaaaaa { -+ [ -+ ttttteeeeeeeeest, -+ ] -+} more { -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -+}": - pass - --if f"aaaaaaaaaaa {[ttttteeeeeeeeest,]} more { -+if f"aaaaaaaaaaa { -+ [ -+ ttttteeeeeeeeest, -+ ] -+} more { - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - }": - pass - - # For loops --for a in f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -- expression}moreeeeeeeeeeeeeeeeeeee": -+for a in f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeeeeee": - pass - - for a in ( -@@ -521,7 +566,8 @@ - pass - - for a in f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -- expression}moreeeeeeeeeeeeeeeeeeeeeeeeeeeeee": -+ expression -+}moreeeeeeeeeeeeeeeeeeeeeeeeeeeeee": - pass - - for a in ( -@@ -530,8 +576,7 @@ - pass - - # With statements --with f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -- expression}moreeeeeeeeeeeeeeeeeeee": -+with f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{expression}moreeeeeeeeeeeeeeeeeeee": - pass - - with ( -@@ -540,7 +585,8 @@ - pass - - with f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{ -- expression}moreeeeeeeeeeeeeeeeeeeeeeeeeeeeee": -+ expression -+}moreeeeeeeeeeeeeeeeeeeeeeeeeeeeee": - pass - - with ( -@@ -549,78 +595,80 @@ - pass - - # Assert statements --assert f"aaaaaaaaa{ -- expression}bbbbbbbbbbbb", f"cccccccccc{ -- expression}dddddddddd" -+assert f"aaaaaaaaa{expression}bbbbbbbbbbbb", f"cccccccccc{expression}dddddddddd" - - assert f"aaaaaaaaa{expression}bbbbbbbbbbbb", f"cccccccccccccccc{ -- expression}dddddddddddddddd" -+ expression -+}dddddddddddddddd" - --assert ( -- f"aaaaaaaaa{expression}bbbbbbbbbbbb" --), f"cccccccccccccccc{expression}dddddddddddddddd" -+assert f"aaaaaaaaa{expression}bbbbbbbbbbbb", ( -+ f"cccccccccccccccc{expression}dddddddddddddddd" -+) - - assert f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{ -- expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", ( -- f"ccccccc{expression}dddddddddd" --) -+ expression -+}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", f"ccccccc{expression}dddddddddd" - - assert ( - f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" - ), f"ccccccc{expression}dddddddddd" - - assert f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{ -- expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", f"ccccccccccccccccccccc { -- expression} dddddddddddddddddddddddddd" -+ expression -+}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", f"ccccccccccccccccccccc { -+ expression -+} dddddddddddddddddddddddddd" - --assert f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", f"cccccccccccccccccccccccccccccccc {expression} ddddddddddddddddddddddddddddddddddddd" -+assert ( -+ f"aaaaaaaaaaaaaaaaaaaaaaaaaaa{expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" -+), ( -+ f"cccccccccccccccccccccccccccccccc {expression} ddddddddddddddddddddddddddddddddddddd" -+) - - # F-strings as a single argument to a call expression to test whether it's huggable or not. --call( -- f"{ -- testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee --}" --) -+call(f"{testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee}") - - call( -- f"{ -- testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee --}" -+ f"{testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee}" - ) - - call( - f"{ # comment -- testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee --}" -+ testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -+ }" - ) - - call( - f"""aaaaaaaaaaaaaaaa bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee}""" - ) - --call(f"""aaaaaaaaaaaaaaaa bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -- }""") -+call( -+ f"""aaaaaaaaaaaaaaaa bbbbbbbbbb { -+ testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -+ }""" -+) - - call(f"""aaaaaaaaaaaaaaaa -- bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -- }""") -+ bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee}""") - - call(f"""aaaaaaaaaaaaaaaa -- bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee # comment -- }""") -+ bbbbbbbbbb { -+ testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee # comment -+}""") - - call( - f"""aaaaaaaaaaaaaaaa -- bbbbbbbbbb {testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee # comment -- }""" -+ bbbbbbbbbb { -+ testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee # comment -+ }""" - ) - - call( - f"{ -- aaaaaa -- + '''test -+ aaaaaa -+ + '''test - more''' --}" -+ }" - ) - - # Indentation -@@ -632,37 +680,37 @@ - if indent2: - foo = f"""hello world - hello { -- f"aaaaaaa { -- [ -- 'aaaaaaaaaaaaaaaaaaaaa', -- 'bbbbbbbbbbbbbbbbbbbbb', -- 'ccccccccccccccccccccc', -- 'ddddddddddddddddddddd' -- ] -- } bbbbbbbb" + -- [ -- 'aaaaaaaaaaaaaaaaaaaaa', -- 'bbbbbbbbbbbbbbbbbbbbb', -- 'ccccccccccccccccccccc', -- 'ddddddddddddddddddddd' -- ] -- } -------- -+ f"aaaaaaa { -+ [ -+ 'aaaaaaaaaaaaaaaaaaaaa', -+ 'bbbbbbbbbbbbbbbbbbbbb', -+ 'ccccccccccccccccccccc', -+ 'ddddddddddddddddddddd', -+ ] -+ } bbbbbbbb" -+ + [ -+ "aaaaaaaaaaaaaaaaaaaaa", -+ "bbbbbbbbbbbbbbbbbbbbb", -+ "ccccccccccccccccccccc", -+ "ddddddddddddddddddddd", -+ ] -+ } -------- - """ - - - # Implicit concatenated f-string containing quotes - _ = ( -- 'This string should change its quotes to double quotes' -- f'This string uses double quotes in an expression {"it's a quote"}' -- f'This f-string does not use any quotes.' -+ "This string should change its quotes to double quotes" -+ f"This string uses double quotes in an expression {"it's a quote"}" -+ f"This f-string does not use any quotes." - ) - - # Regression test for https://github.com/astral-sh/ruff/issues/14487 - f"aaaaaaaaaaaaaaaaaaaaaaaaaa {10**27} bbbbbbbbbbbbbbbbbbbbbbbbbb ccccccccccccccccccccccccc" - - # Regression test for https://github.com/astral-sh/ruff/issues/14778 --f"{'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'a' if True else ""}" --f"{'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'a' if True else ""}" -+f"{'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' if True else ''}" -+f"{'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' if True else ''}" - - # Quotes reuse - f"{'a'}" -@@ -682,18 +730,18 @@ - f'{1: hy "user"}' - f'{1:hy "user"}' - f'{1: abcd "{1}" }' --f'{1: abcd "{'aa'}" }' -+f'{1: abcd "{"aa"}" }' - f'{1=: "abcd {'aa'}}' --f'{x:a{z:hy "user"}} \'\'\'' -+f"{x:a{z:hy \"user\"}} '''" - - # Changing the outer quotes is fine because the format-spec is in a nested expression. --f'{f'{z=:hy "user"}'} \'\'\'' -+f"{f'{z=:hy "user"}'} '''" - - - # We have to be careful about changing the quotes if the f-string has a debug expression because it is inserted verbatim. - f'{1=: "abcd \'\'}' # Don't change the outer quotes, or it results in a syntax error --f'{1=: abcd \'\'}' # Changing the quotes here is fine because the inner quotes aren't the opposite quotes --f'{1=: abcd \"\"}' # Changing the quotes here is fine because the inner quotes are escaped -+f"{1=: abcd \'\'}" # Changing the quotes here is fine because the inner quotes aren't the opposite quotes -+f"{1=: abcd \"\"}" # Changing the quotes here is fine because the inner quotes are escaped - # Don't change the quotes in the following cases: - f'{x=:hy "user"} \'\'\'' - f'{x=:a{y:hy "user"}} \'\'\'' -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string.py.snap index 46cfbbd167..c2e7f51ca1 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string.py.snap @@ -355,22 +355,7 @@ f"aaaaaaaaaaaaaaaa \ ) ``` -## Outputs -### Output 1 -``` -indent-style = space -line-width = 88 -indent-width = 4 -quote-style = Double -line-ending = LineFeed -magic-trailing-comma = Respect -docstring-code = Disabled -docstring-code-line-width = "dynamic" -preview = Enabled -target_version = Py39 -source_type = Python -``` - +## Output ```python "aaaaaaaaabbbbbbbbbbbbbbbbbbbb" # Join @@ -616,12 +601,9 @@ assert ( ############################################################################## # Use can_omit_optional_parentheses layout to avoid an instability where the formatter # picks the can_omit_optional_parentheses layout when the strings are joined. -if f"implicitconcatenatedstring" + f"implicitconcadddddddddddedring" * len([ - aaaaaa, - bbbbbbbbbbbbbbbb, - cccccccccccccccccc, - ddddddddddddddddddddddddddd, -]): +if f"implicitconcatenatedstring" + f"implicitconcadddddddddddedring" * len( + [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd] +): pass # Keep parenthesizing multiline - implicit concatenated strings @@ -792,3 +774,26 @@ f"aaaaaaaaaaaaaaaa \ ddddddddddddddddddd" # comment 4 ) ``` + + +## Preview changes +```diff +--- Stable ++++ Preview +@@ -242,9 +242,12 @@ + ############################################################################## + # Use can_omit_optional_parentheses layout to avoid an instability where the formatter + # picks the can_omit_optional_parentheses layout when the strings are joined. +-if f"implicitconcatenatedstring" + f"implicitconcadddddddddddedring" * len( +- [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd] +-): ++if f"implicitconcatenatedstring" + f"implicitconcadddddddddddedring" * len([ ++ aaaaaa, ++ bbbbbbbbbbbbbbbb, ++ cccccccccccccccccc, ++ ddddddddddddddddddddddddddd, ++]): + pass + + # Keep parenthesizing multiline - implicit concatenated strings +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_assignment.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_assignment.py.snap index 641f53d963..b30f507d8d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_assignment.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_assignment.py.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_assignment.py -snapshot_kind: text --- ## Input ```python @@ -300,22 +299,7 @@ aaaaa[aaaaaaaaaaa] = ( ) ``` -## Outputs -### Output 1 -``` -indent-style = space -line-width = 88 -indent-width = 4 -quote-style = Double -line-ending = LineFeed -magic-trailing-comma = Respect -docstring-code = Disabled -docstring-code-line-width = "dynamic" -preview = Enabled -target_version = Py39 -source_type = Python -``` - +## Output ```python ## Implicit concatenated strings with a trailing comment but a non splittable target. diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap index 848aa3e45f..f14ff125f6 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_preserve.py -snapshot_kind: text --- ## Input ```python @@ -31,7 +30,7 @@ line-ending = LineFeed magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" -preview = Enabled +preview = Disabled target_version = Py39 source_type = Python ``` @@ -63,7 +62,7 @@ line-ending = LineFeed magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" -preview = Enabled +preview = Disabled target_version = Py312 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__list_comp.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__list_comp.py.snap index b40606ac69..0dcdb03493 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__list_comp.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__list_comp.py.snap @@ -323,24 +323,20 @@ aaaaaaaaaaaaaaaaaaaaa = [ # Leading expression comments: y = [ a - for - ( + for ( # comment a - ) in - ( + ) in ( # comment x ) - if - ( + if ( # asdasd "askldaklsdnmklasmdlkasmdlkasmdlkasmdasd" != "as,mdnaskldmlkasdmlaksdmlkasdlkasdm" and "zxcm,.nzxclm,zxnckmnzxckmnzxczxc" != "zxcasdasdlmnasdlknaslkdnmlaskdm" ) - if - ( + if ( # comment x ) @@ -383,39 +379,3 @@ y = [ x ] ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -142,24 +142,20 @@ - # Leading expression comments: - y = [ - a -- for -- ( -+ for ( - # comment - a -- ) in -- ( -+ ) in ( - # comment - x - ) -- if -- ( -+ if ( - # asdasd - "askldaklsdnmklasmdlkasmdlkasmdlkasmdasd" - != "as,mdnaskldmlkasdmlaksdmlkasdlkasdm" - and "zxcm,.nzxclm,zxnckmnzxckmnzxczxc" != "zxcasdasdlmnasdlknaslkdnmlaskdm" - ) -- if -- ( -+ if ( - # comment - x - ) -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap index f5ae95c77b..fa68fdbfcb 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap @@ -310,9 +310,9 @@ String \"\"\" # String continuation -"Let's" "start" "with" "a" "simple" "example" +"Let'sstartwithasimpleexample" -"Let's" "start" "with" "a" "simple" "example" "now repeat after me:" "I am confident" "I am confident" "I am confident" "I am confident" "I am confident" +"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident" ( "Let's" @@ -379,7 +379,7 @@ test_particular = [ ] # Parenthesized string continuation with messed up indentation -{"key": ([], "a" "b" "c")} +{"key": ([], "abc")} # Regression test for https://github.com/astral-sh/ruff/issues/5893 @@ -412,19 +412,31 @@ a = """\\\x1f""" # In preview, don't collapse implicit concatenated strings that can't be joined into a single string # and that are multiline in the source. -(r"aaaaaaaaa" r"bbbbbbbbbbbbbbbbbbbb") - -(r"aaaaaaaaa" r"bbbbbbbbbbbbbbbbbbbb" "cccccccccccccccccccccc") - -("""aaaaaaaaa""" """bbbbbbbbbbbbbbbbbbbb""") +( + r"aaaaaaaaa" + r"bbbbbbbbbbbbbbbbbbbb" +) ( - f"""aaaa{ - 10}aaaaa""" + r"aaaaaaaaa" + r"bbbbbbbbbbbbbbbbbbbb" + "cccccccccccccccccccccc" +) + +( + """aaaaaaaaa""" + """bbbbbbbbbbbbbbbbbbbb""" +) + +( + f"""aaaa{10}aaaaa""" rf"""bbbbbbbbbbbbbbbbbbbb""" ) -if (r"aaaaaaaaa" r"bbbbbbbbbbbbbbbbbbbb") + ["aaaaaaaaaa", "bbbbbbbbbbbbbbbb"]: +if ( + r"aaaaaaaaa" + r"bbbbbbbbbbbbbbbbbbbb" +) + ["aaaaaaaaaa", "bbbbbbbbbbbbbbbb"]: ... @@ -433,24 +445,19 @@ if (r"aaaaaaaaa" r"bbbbbbbbbbbbbbbbbbbb") + ["aaaaaaaaaa", "bbbbbbbbbbbbbbbb"]: r"aaaaaaaaa" r"bbbbbbbbbbbbbbbbbbbb" -( - f"aaaa{ - 10}aaaaa" - rf"bbbbbbbbbbbbbbbbbbbb" -) +(f"aaaa{10}aaaaa" rf"bbbbbbbbbbbbbbbbbbbb") (r"""aaaaaaaaa""" r"""bbbbbbbbbbbbbbbbbbbb""") -( - f"""aaaa{ - 10}aaaaa""" - rf"""bbbbbbbbbbbbbbbbbbbb""" -) +(f"""aaaa{10}aaaaa""" rf"""bbbbbbbbbbbbbbbbbbbb""") # In docstring positions def docstring(): - r"aaaaaaaaa" "bbbbbbbbbbbbbbbbbbbb" + ( + r"aaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbb" + ) def docstring_flat(): @@ -465,103 +472,6 @@ def docstring_flat_overlong(): ``` -#### Preview changes -```diff ---- Stable -+++ Preview -@@ -70,9 +70,9 @@ - - # String continuation - --"Let's" "start" "with" "a" "simple" "example" -+"Let'sstartwithasimpleexample" - --"Let's" "start" "with" "a" "simple" "example" "now repeat after me:" "I am confident" "I am confident" "I am confident" "I am confident" "I am confident" -+"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident" - - ( - "Let's" -@@ -139,7 +139,7 @@ - ] - - # Parenthesized string continuation with messed up indentation --{"key": ([], "a" "b" "c")} -+{"key": ([], "abc")} - - - # Regression test for https://github.com/astral-sh/ruff/issues/5893 -@@ -172,19 +172,31 @@ - # In preview, don't collapse implicit concatenated strings that can't be joined into a single string - # and that are multiline in the source. - --(r"aaaaaaaaa" r"bbbbbbbbbbbbbbbbbbbb") -+( -+ r"aaaaaaaaa" -+ r"bbbbbbbbbbbbbbbbbbbb" -+) - --(r"aaaaaaaaa" r"bbbbbbbbbbbbbbbbbbbb" "cccccccccccccccccccccc") -+( -+ r"aaaaaaaaa" -+ r"bbbbbbbbbbbbbbbbbbbb" -+ "cccccccccccccccccccccc" -+) - --("""aaaaaaaaa""" """bbbbbbbbbbbbbbbbbbbb""") -+( -+ """aaaaaaaaa""" -+ """bbbbbbbbbbbbbbbbbbbb""" -+) - - ( -- f"""aaaa{ -- 10}aaaaa""" -+ f"""aaaa{10}aaaaa""" - rf"""bbbbbbbbbbbbbbbbbbbb""" - ) - --if (r"aaaaaaaaa" r"bbbbbbbbbbbbbbbbbbbb") + ["aaaaaaaaaa", "bbbbbbbbbbbbbbbb"]: -+if ( -+ r"aaaaaaaaa" -+ r"bbbbbbbbbbbbbbbbbbbb" -+) + ["aaaaaaaaaa", "bbbbbbbbbbbbbbbb"]: - ... - - -@@ -193,24 +205,19 @@ - - r"aaaaaaaaa" r"bbbbbbbbbbbbbbbbbbbb" - --( -- f"aaaa{ -- 10}aaaaa" -- rf"bbbbbbbbbbbbbbbbbbbb" --) -+(f"aaaa{10}aaaaa" rf"bbbbbbbbbbbbbbbbbbbb") - - (r"""aaaaaaaaa""" r"""bbbbbbbbbbbbbbbbbbbb""") - --( -- f"""aaaa{ -- 10}aaaaa""" -- rf"""bbbbbbbbbbbbbbbbbbbb""" --) -+(f"""aaaa{10}aaaaa""" rf"""bbbbbbbbbbbbbbbbbbbb""") - - - # In docstring positions - def docstring(): -- r"aaaaaaaaa" "bbbbbbbbbbbbbbbbbbbb" -+ ( -+ r"aaaaaaaaa" -+ "bbbbbbbbbbbbbbbbbbbb" -+ ) - - - def docstring_flat(): -``` - - ### Output 2 ``` indent-style = space @@ -650,9 +560,9 @@ String \"\"\" # String continuation -"Let's" 'start' 'with' 'a' 'simple' 'example' +"Let'sstartwithasimpleexample" -"Let's" 'start' 'with' 'a' 'simple' 'example' 'now repeat after me:' 'I am confident' 'I am confident' 'I am confident' 'I am confident' 'I am confident' +"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident" ( "Let's" @@ -719,7 +629,7 @@ test_particular = [ ] # Parenthesized string continuation with messed up indentation -{'key': ([], 'a' 'b' 'c')} +{'key': ([], 'abc')} # Regression test for https://github.com/astral-sh/ruff/issues/5893 @@ -752,19 +662,31 @@ a = """\\\x1f""" # In preview, don't collapse implicit concatenated strings that can't be joined into a single string # and that are multiline in the source. -(r'aaaaaaaaa' r'bbbbbbbbbbbbbbbbbbbb') - -(r'aaaaaaaaa' r'bbbbbbbbbbbbbbbbbbbb' 'cccccccccccccccccccccc') - -("""aaaaaaaaa""" """bbbbbbbbbbbbbbbbbbbb""") +( + r'aaaaaaaaa' + r'bbbbbbbbbbbbbbbbbbbb' +) ( - f"""aaaa{ - 10}aaaaa""" + r'aaaaaaaaa' + r'bbbbbbbbbbbbbbbbbbbb' + 'cccccccccccccccccccccc' +) + +( + """aaaaaaaaa""" + """bbbbbbbbbbbbbbbbbbbb""" +) + +( + f"""aaaa{10}aaaaa""" rf"""bbbbbbbbbbbbbbbbbbbb""" ) -if (r'aaaaaaaaa' r'bbbbbbbbbbbbbbbbbbbb') + ['aaaaaaaaaa', 'bbbbbbbbbbbbbbbb']: +if ( + r'aaaaaaaaa' + r'bbbbbbbbbbbbbbbbbbbb' +) + ['aaaaaaaaaa', 'bbbbbbbbbbbbbbbb']: ... @@ -773,24 +695,19 @@ if (r'aaaaaaaaa' r'bbbbbbbbbbbbbbbbbbbb') + ['aaaaaaaaaa', 'bbbbbbbbbbbbbbbb']: r'aaaaaaaaa' r'bbbbbbbbbbbbbbbbbbbb' -( - f'aaaa{ - 10}aaaaa' - rf'bbbbbbbbbbbbbbbbbbbb' -) +(f'aaaa{10}aaaaa' rf'bbbbbbbbbbbbbbbbbbbb') (r"""aaaaaaaaa""" r"""bbbbbbbbbbbbbbbbbbbb""") -( - f"""aaaa{ - 10}aaaaa""" - rf"""bbbbbbbbbbbbbbbbbbbb""" -) +(f"""aaaa{10}aaaaa""" rf"""bbbbbbbbbbbbbbbbbbbb""") # In docstring positions def docstring(): - r'aaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbb' + ( + r'aaaaaaaaa' + 'bbbbbbbbbbbbbbbbbbbb' + ) def docstring_flat(): @@ -803,100 +720,3 @@ def docstring_flat_overlong(): r'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' ) ``` - - -#### Preview changes -```diff ---- Stable -+++ Preview -@@ -70,9 +70,9 @@ - - # String continuation - --"Let's" 'start' 'with' 'a' 'simple' 'example' -+"Let'sstartwithasimpleexample" - --"Let's" 'start' 'with' 'a' 'simple' 'example' 'now repeat after me:' 'I am confident' 'I am confident' 'I am confident' 'I am confident' 'I am confident' -+"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident" - - ( - "Let's" -@@ -139,7 +139,7 @@ - ] - - # Parenthesized string continuation with messed up indentation --{'key': ([], 'a' 'b' 'c')} -+{'key': ([], 'abc')} - - - # Regression test for https://github.com/astral-sh/ruff/issues/5893 -@@ -172,19 +172,31 @@ - # In preview, don't collapse implicit concatenated strings that can't be joined into a single string - # and that are multiline in the source. - --(r'aaaaaaaaa' r'bbbbbbbbbbbbbbbbbbbb') -+( -+ r'aaaaaaaaa' -+ r'bbbbbbbbbbbbbbbbbbbb' -+) - --(r'aaaaaaaaa' r'bbbbbbbbbbbbbbbbbbbb' 'cccccccccccccccccccccc') -+( -+ r'aaaaaaaaa' -+ r'bbbbbbbbbbbbbbbbbbbb' -+ 'cccccccccccccccccccccc' -+) - --("""aaaaaaaaa""" """bbbbbbbbbbbbbbbbbbbb""") -+( -+ """aaaaaaaaa""" -+ """bbbbbbbbbbbbbbbbbbbb""" -+) - - ( -- f"""aaaa{ -- 10}aaaaa""" -+ f"""aaaa{10}aaaaa""" - rf"""bbbbbbbbbbbbbbbbbbbb""" - ) - --if (r'aaaaaaaaa' r'bbbbbbbbbbbbbbbbbbbb') + ['aaaaaaaaaa', 'bbbbbbbbbbbbbbbb']: -+if ( -+ r'aaaaaaaaa' -+ r'bbbbbbbbbbbbbbbbbbbb' -+) + ['aaaaaaaaaa', 'bbbbbbbbbbbbbbbb']: - ... - - -@@ -193,24 +205,19 @@ - - r'aaaaaaaaa' r'bbbbbbbbbbbbbbbbbbbb' - --( -- f'aaaa{ -- 10}aaaaa' -- rf'bbbbbbbbbbbbbbbbbbbb' --) -+(f'aaaa{10}aaaaa' rf'bbbbbbbbbbbbbbbbbbbb') - - (r"""aaaaaaaaa""" r"""bbbbbbbbbbbbbbbbbbbb""") - --( -- f"""aaaa{ -- 10}aaaaa""" -- rf"""bbbbbbbbbbbbbbbbbbbb""" --) -+(f"""aaaa{10}aaaaa""" rf"""bbbbbbbbbbbbbbbbbbbb""") - - - # In docstring positions - def docstring(): -- r'aaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbb' -+ ( -+ r'aaaaaaaaa' -+ 'bbbbbbbbbbbbbbbbbbbb' -+ ) - - - def docstring_flat(): -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__yield.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__yield.py.snap index 32b57b37b1..8f0589d5c3 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__yield.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__yield.py.snap @@ -210,7 +210,7 @@ yield ( ) ) -yield "Cache key will cause errors if used with memcached: %r " "(longer than %s)" % ( +yield "Cache key will cause errors if used with memcached: %r (longer than %s)" % ( key, MEMCACHE_MAX_KEY_LENGTH, ) @@ -228,8 +228,7 @@ yield ( "Django to create, modify, and delete the table" ) yield ( - "# Feel free to rename the models, but don't rename db_table values or " - "field names." + "# Feel free to rename the models, but don't rename db_table values or field names." ) yield ( @@ -241,8 +240,7 @@ yield ( "Django to create, modify, and delete the table" ) yield ( - "# Feel free to rename the models, but don't rename db_table values or " - "field names." + "# Feel free to rename the models, but don't rename db_table values or field names." ) # Regression test for: https://github.com/astral-sh/ruff/issues/7420 @@ -278,39 +276,3 @@ result = yield ( print((yield x)) ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -78,7 +78,7 @@ - ) - ) - --yield "Cache key will cause errors if used with memcached: %r " "(longer than %s)" % ( -+yield "Cache key will cause errors if used with memcached: %r (longer than %s)" % ( - key, - MEMCACHE_MAX_KEY_LENGTH, - ) -@@ -96,8 +96,7 @@ - "Django to create, modify, and delete the table" - ) - yield ( -- "# Feel free to rename the models, but don't rename db_table values or " -- "field names." -+ "# Feel free to rename the models, but don't rename db_table values or field names." - ) - - yield ( -@@ -109,8 +108,7 @@ - "Django to create, modify, and delete the table" - ) - yield ( -- "# Feel free to rename the models, but don't rename db_table values or " -- "field names." -+ "# Feel free to rename the models, but don't rename db_table values or field names." - ) - - # Regression test for: https://github.com/astral-sh/ruff/issues/7420 -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@pattern__pattern_maybe_parenthesize.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@pattern__pattern_maybe_parenthesize.py.snap index 80ba16a98f..45fe2da38d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@pattern__pattern_maybe_parenthesize.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@pattern__pattern_maybe_parenthesize.py.snap @@ -304,31 +304,43 @@ match x: # Patterns that use BestFit should be parenthesized if they exceed the configured line width # but fit within parentheses. match x: - case "averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPar": + case ( + "averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPar" + ): pass match x: - case b"averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPa": + case ( + b"averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPa" + ): pass match x: - case f"averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPa": + case ( + f"averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPa" + ): pass match x: - case 5444444444444444444444444444444444444444444444444444444444444444444444444444444j: + case ( + 5444444444444444444444444444444444444444444444444444444444444444444444444444444j + ): pass match x: - case 5444444444444444444444444444444444444444444444444444444444444444444444444444444: + case ( + 5444444444444444444444444444444444444444444444444444444444444444444444444444444 + ): pass match x: - case 5.44444444444444444444444444444444444444444444444444444444444444444444444444444: + case ( + 5.44444444444444444444444444444444444444444444444444444444444444444444444444444 + ): pass @@ -385,108 +397,89 @@ match x: match x: - case "an implicit concatenated" "string literal" "in a match case" "that goes over multiple lines": + case ( + "an implicit concatenated" + "string literal" + "in a match case" + "that goes over multiple lines" + ): pass ## Patterns ending with a sequence, mapping, class, or parenthesized pattern should break the parenthesized-like pattern first match x: - case ( - A - | [ - aaaaaa, - bbbbbbbbbbbbbbbb, - cccccccccccccccccc, - ddddddddddddddddddddddddddd, - ] - ): + case A | [ + aaaaaa, + bbbbbbbbbbbbbbbb, + cccccccccccccccccc, + ddddddddddddddddddddddddddd, + ]: pass match x: - case ( - A - | ( - aaaaaa, - bbbbbbbbbbbbbbbb, - cccccccccccccccccc, - ddddddddddddddddddddddddddd, - ) + case A | ( + aaaaaa, + bbbbbbbbbbbbbbbb, + cccccccccccccccccc, + ddddddddddddddddddddddddddd, ): pass match x: - case ( - A - | { - "a": aaaaaa, - "b": bbbbbbbbbbbbbbbb, - "c": cccccccccccccccccc, - "d": ddddddddddddddddddddddddddd, - } + case A | { + "a": aaaaaa, + "b": bbbbbbbbbbbbbbbb, + "c": cccccccccccccccccc, + "d": ddddddddddddddddddddddddddd, + }: + pass + + +match x: + case A | Class( + aaaaaa, + bbbbbbbbbbbbbbbb, + cccccccccccccccccc, + ddddddddddddddddddddddddddd, ): pass match x: - case ( - A - | Class( - aaaaaa, - bbbbbbbbbbbbbbbb, - cccccccccccccccccc, - ddddddddddddddddddddddddddd, - ) - ): - pass - - -match x: - case ( - A - | ( - aaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbb.cccccccccccccccccccccccccccc.ddddddddddddddddddddddd - ) + case A | ( + aaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbb.cccccccccccccccccccccccccccc.ddddddddddddddddddddddd ): pass ## Patterns starting with a sequence, mapping, class, or parenthesized pattern should break the parenthesized-like pattern first match x: - case ( - [ - aaaaaa, - bbbbbbbbbbbbbbbb, - cccccccccccccccccc, - ddddddddddddddddddddddddddd, - ] - | A - ): + case [ + aaaaaa, + bbbbbbbbbbbbbbbb, + cccccccccccccccccc, + ddddddddddddddddddddddddddd, + ] | A: pass match x: case ( - ( - aaaaaa, - bbbbbbbbbbbbbbbb, - cccccccccccccccccc, - ddddddddddddddddddddddddddd, - ) - | A - ): + aaaaaa, + bbbbbbbbbbbbbbbb, + cccccccccccccccccc, + ddddddddddddddddddddddddddd, + ) | A: pass match x: - case ( - { - "a": aaaaaa, - "b": bbbbbbbbbbbbbbbb, - "c": cccccccccccccccccc, - "d": ddddddddddddddddddddddddddd, - } - | A - ): + case { + "a": aaaaaa, + "b": bbbbbbbbbbbbbbbb, + "c": cccccccccccccccccc, + "d": ddddddddddddddddddddddddddd, + } | A: pass @@ -503,8 +496,7 @@ match x: ## Not for non-parenthesized sequence patterns match x: case ( - (1) - | aaaaaaaaaaaaaaaaaaaaaaaaaaaa, + (1) | aaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccccccccccccccc, ): @@ -523,448 +515,81 @@ match x: ## Always use parentheses for implicitly concatenated strings match x: - case ( - "implicit" "concatenated" "string" - | [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd] - ): + case "implicitconcatenatedstring" | [ + aaaaaa, + bbbbbbbbbbbbbbbb, + cccccccccccccccccc, + ddddddddddddddddddddddddddd, + ]: pass match x: - case ( - b"implicit" b"concatenated" b"string" - | [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd] - ): + case b"implicitconcatenatedstring" | [ + aaaaaa, + bbbbbbbbbbbbbbbb, + cccccccccccccccccc, + ddddddddddddddddddddddddddd, + ]: pass match x: - case ( - f"implicit" "concatenated" "string" - | [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd] - ): + case f"implicitconcatenatedstring" | [ + aaaaaa, + bbbbbbbbbbbbbbbb, + cccccccccccccccccc, + ddddddddddddddddddddddddddd, + ]: pass ## Complex number expressions and unary expressions match x: - case ( - 4 - 3j - | [ - aaaaaaaaaaaaaaaaaaaaaaaa, - bbbbbbbbbbbbbbbbbbbbbbbbbbbb, - cccccccccccccccccccccccccccccccccccccccc, - ] - ): + case 4 - 3j | [ + aaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccccccccccccccccc, + ]: pass match x: - case ( - 4 + 3j - | [ - aaaaaaaaaaaaaaaaaaaaaaaa, - bbbbbbbbbbbbbbbbbbbbbbbbbbbb, - cccccccccccccccccccccccccccccccccccccccc, - ] - ): + case 4 + 3j | [ + aaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccccccccccccccccc, + ]: pass match x: - case ( - -1 - | [ - aaaaaaaaaaaaaaaaaaaaaaaaaaaa, - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, - ccccccccccccccccccccccccccccccccc, - ] - ): + case -1 | [ + aaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ccccccccccccccccccccccccccccccccc, + ]: pass ### Parenthesized patterns match x: - case ( - (1) - | [ - aaaaaaaaaaaaaaaaaaaaaaaaaaaa, - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, - ccccccccccccccccccccccccccccccccc, - ] - ): + case (1) | [ + aaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ccccccccccccccccccccccccccccccccc, + ]: pass match x: - case ( - ( # comment - 1 - ) - | [ - aaaaaaaaaaaaaaaaaaaaaaaaaaaa, - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, - ccccccccccccccccccccccccccccccccc, - ] - ): + case ( # comment + 1 + ) | [ + aaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + ccccccccccccccccccccccccccccccccc, + ]: pass ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -1,31 +1,43 @@ - # Patterns that use BestFit should be parenthesized if they exceed the configured line width - # but fit within parentheses. - match x: -- case "averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPar": -+ case ( -+ "averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPar" -+ ): - pass - - - match x: -- case b"averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPa": -+ case ( -+ b"averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPa" -+ ): - pass - - match x: -- case f"averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPa": -+ case ( -+ f"averyLongStringThatGetsParenthesizedOnceItExceedsTheConfiguredLineWidthFitsPa" -+ ): - pass - - - match x: -- case 5444444444444444444444444444444444444444444444444444444444444444444444444444444j: -+ case ( -+ 5444444444444444444444444444444444444444444444444444444444444444444444444444444j -+ ): - pass - - - match x: -- case 5444444444444444444444444444444444444444444444444444444444444444444444444444444: -+ case ( -+ 5444444444444444444444444444444444444444444444444444444444444444444444444444444 -+ ): - pass - - - match x: -- case 5.44444444444444444444444444444444444444444444444444444444444444444444444444444: -+ case ( -+ 5.44444444444444444444444444444444444444444444444444444444444444444444444444444 -+ ): - pass - - -@@ -82,108 +94,89 @@ - - - match x: -- case "an implicit concatenated" "string literal" "in a match case" "that goes over multiple lines": -+ case ( -+ "an implicit concatenated" -+ "string literal" -+ "in a match case" -+ "that goes over multiple lines" -+ ): - pass - - - ## Patterns ending with a sequence, mapping, class, or parenthesized pattern should break the parenthesized-like pattern first - match x: -- case ( -- A -- | [ -- aaaaaa, -- bbbbbbbbbbbbbbbb, -- cccccccccccccccccc, -- ddddddddddddddddddddddddddd, -- ] -- ): -+ case A | [ -+ aaaaaa, -+ bbbbbbbbbbbbbbbb, -+ cccccccccccccccccc, -+ ddddddddddddddddddddddddddd, -+ ]: - pass - - match x: -- case ( -- A -- | ( -- aaaaaa, -- bbbbbbbbbbbbbbbb, -- cccccccccccccccccc, -- ddddddddddddddddddddddddddd, -- ) -+ case A | ( -+ aaaaaa, -+ bbbbbbbbbbbbbbbb, -+ cccccccccccccccccc, -+ ddddddddddddddddddddddddddd, - ): - pass - - - match x: -- case ( -- A -- | { -- "a": aaaaaa, -- "b": bbbbbbbbbbbbbbbb, -- "c": cccccccccccccccccc, -- "d": ddddddddddddddddddddddddddd, -- } -- ): -+ case A | { -+ "a": aaaaaa, -+ "b": bbbbbbbbbbbbbbbb, -+ "c": cccccccccccccccccc, -+ "d": ddddddddddddddddddddddddddd, -+ }: - pass - - - match x: -- case ( -- A -- | Class( -- aaaaaa, -- bbbbbbbbbbbbbbbb, -- cccccccccccccccccc, -- ddddddddddddddddddddddddddd, -- ) -+ case A | Class( -+ aaaaaa, -+ bbbbbbbbbbbbbbbb, -+ cccccccccccccccccc, -+ ddddddddddddddddddddddddddd, - ): - pass - - - match x: -- case ( -- A -- | ( -- aaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbb.cccccccccccccccccccccccccccc.ddddddddddddddddddddddd -- ) -+ case A | ( -+ aaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbb.cccccccccccccccccccccccccccc.ddddddddddddddddddddddd - ): - pass - - - ## Patterns starting with a sequence, mapping, class, or parenthesized pattern should break the parenthesized-like pattern first - match x: -- case ( -- [ -- aaaaaa, -- bbbbbbbbbbbbbbbb, -- cccccccccccccccccc, -- ddddddddddddddddddddddddddd, -- ] -- | A -- ): -+ case [ -+ aaaaaa, -+ bbbbbbbbbbbbbbbb, -+ cccccccccccccccccc, -+ ddddddddddddddddddddddddddd, -+ ] | A: - pass - - match x: - case ( -- ( -- aaaaaa, -- bbbbbbbbbbbbbbbb, -- cccccccccccccccccc, -- ddddddddddddddddddddddddddd, -- ) -- | A -- ): -+ aaaaaa, -+ bbbbbbbbbbbbbbbb, -+ cccccccccccccccccc, -+ ddddddddddddddddddddddddddd, -+ ) | A: - pass - - - match x: -- case ( -- { -- "a": aaaaaa, -- "b": bbbbbbbbbbbbbbbb, -- "c": cccccccccccccccccc, -- "d": ddddddddddddddddddddddddddd, -- } -- | A -- ): -+ case { -+ "a": aaaaaa, -+ "b": bbbbbbbbbbbbbbbb, -+ "c": cccccccccccccccccc, -+ "d": ddddddddddddddddddddddddddd, -+ } | A: - pass - - -@@ -200,8 +193,7 @@ - ## Not for non-parenthesized sequence patterns - match x: - case ( -- (1) -- | aaaaaaaaaaaaaaaaaaaaaaaaaaaa, -+ (1) | aaaaaaaaaaaaaaaaaaaaaaaaaaaa, - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, - ccccccccccccccccccccccccccccccccc, - ): -@@ -220,89 +212,80 @@ - - ## Always use parentheses for implicitly concatenated strings - match x: -- case ( -- "implicit" "concatenated" "string" -- | [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd] -- ): -+ case "implicitconcatenatedstring" | [ -+ aaaaaa, -+ bbbbbbbbbbbbbbbb, -+ cccccccccccccccccc, -+ ddddddddddddddddddddddddddd, -+ ]: - pass - - - match x: -- case ( -- b"implicit" b"concatenated" b"string" -- | [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd] -- ): -+ case b"implicitconcatenatedstring" | [ -+ aaaaaa, -+ bbbbbbbbbbbbbbbb, -+ cccccccccccccccccc, -+ ddddddddddddddddddddddddddd, -+ ]: - pass - - - match x: -- case ( -- f"implicit" "concatenated" "string" -- | [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd] -- ): -+ case f"implicitconcatenatedstring" | [ -+ aaaaaa, -+ bbbbbbbbbbbbbbbb, -+ cccccccccccccccccc, -+ ddddddddddddddddddddddddddd, -+ ]: - pass - - - ## Complex number expressions and unary expressions - - match x: -- case ( -- 4 - 3j -- | [ -- aaaaaaaaaaaaaaaaaaaaaaaa, -- bbbbbbbbbbbbbbbbbbbbbbbbbbbb, -- cccccccccccccccccccccccccccccccccccccccc, -- ] -- ): -+ case 4 - 3j | [ -+ aaaaaaaaaaaaaaaaaaaaaaaa, -+ bbbbbbbbbbbbbbbbbbbbbbbbbbbb, -+ cccccccccccccccccccccccccccccccccccccccc, -+ ]: - pass - - - match x: -- case ( -- 4 + 3j -- | [ -- aaaaaaaaaaaaaaaaaaaaaaaa, -- bbbbbbbbbbbbbbbbbbbbbbbbbbbb, -- cccccccccccccccccccccccccccccccccccccccc, -- ] -- ): -+ case 4 + 3j | [ -+ aaaaaaaaaaaaaaaaaaaaaaaa, -+ bbbbbbbbbbbbbbbbbbbbbbbbbbbb, -+ cccccccccccccccccccccccccccccccccccccccc, -+ ]: - pass - - - match x: -- case ( -- -1 -- | [ -- aaaaaaaaaaaaaaaaaaaaaaaaaaaa, -- bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, -- ccccccccccccccccccccccccccccccccc, -- ] -- ): -+ case -1 | [ -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaa, -+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, -+ ccccccccccccccccccccccccccccccccc, -+ ]: - pass - - - ### Parenthesized patterns - match x: -- case ( -- (1) -- | [ -- aaaaaaaaaaaaaaaaaaaaaaaaaaaa, -- bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, -- ccccccccccccccccccccccccccccccccc, -- ] -- ): -+ case (1) | [ -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaa, -+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, -+ ccccccccccccccccccccccccccccccccc, -+ ]: - pass - - - match x: -- case ( -- ( # comment -- 1 -- ) -- | [ -- aaaaaaaaaaaaaaaaaaaaaaaaaaaa, -- bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, -- ccccccccccccccccccccccccccccccccc, -- ] -- ): -+ case ( # comment -+ 1 -+ ) | [ -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaa, -+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, -+ ccccccccccccccccccccccccccccccccc, -+ ]: - pass -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap index 61fbba6cbd..9910d923ea 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap @@ -109,10 +109,10 @@ rb"""rb double triple""" rb"""br single triple""" rb"""br double triple""" -'single1' 'single2' -'single1' 'double2' -'double1' 'single2' -'double1' 'double2' +'single1single2' +'single1double2' +'double1single2' +'double1double2' def docstring_single_triple(): @@ -132,28 +132,6 @@ def docstring_single(): ``` -#### Preview changes -```diff ---- Stable -+++ Preview -@@ -33,10 +33,10 @@ - rb"""br single triple""" - rb"""br double triple""" - --'single1' 'single2' --'single1' 'double2' --'double1' 'single2' --'double1' 'double2' -+'single1single2' -+'single1double2' -+'double1single2' -+'double1double2' - - - def docstring_single_triple(): -``` - - ### Output 2 ``` indent-style = space @@ -205,10 +183,10 @@ rb"""rb double triple""" rb"""br single triple""" rb"""br double triple""" -"single1" "single2" -"single1" "double2" -"double1" "single2" -"double1" "double2" +"single1single2" +"single1double2" +"double1single2" +"double1double2" def docstring_single_triple(): @@ -228,28 +206,6 @@ def docstring_single(): ``` -#### Preview changes -```diff ---- Stable -+++ Preview -@@ -33,10 +33,10 @@ - rb"""br single triple""" - rb"""br double triple""" - --"single1" "single2" --"single1" "double2" --"double1" "single2" --"double1" "double2" -+"single1single2" -+"single1double2" -+"double1single2" -+"double1double2" - - - def docstring_single_triple(): -``` - - ### Output 3 ``` indent-style = space @@ -301,10 +257,10 @@ rb"""rb double triple""" rb'''br single triple''' rb"""br double triple""" -'single1' 'single2' -'single1' "double2" -"double1" 'single2' -"double1" "double2" +'single1single2' +'single1double2' +"double1single2" +"double1double2" def docstring_single_triple(): @@ -322,25 +278,3 @@ def docstring_double(): def docstring_single(): 'single' ``` - - -#### Preview changes -```diff ---- Stable -+++ Preview -@@ -33,10 +33,10 @@ - rb'''br single triple''' - rb"""br double triple""" - --'single1' 'single2' --'single1' "double2" --"double1" 'single2' --"double1" "double2" -+'single1single2' -+'single1double2' -+"double1single2" -+"double1double2" - - - def docstring_single_triple(): -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap index 46a70ee098..4b2a7b6f4b 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/range_formatting/stub.pyi -snapshot_kind: text --- ## Input ```python @@ -34,7 +33,7 @@ line-ending = LineFeed magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" -preview = Enabled +preview = Disabled target_version = Py39 source_type = Stub ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__assert.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__assert.py.snap index 648583c0cb..8f4bf41d65 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__assert.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__assert.py.snap @@ -212,50 +212,47 @@ assert ( def test(): - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected - ), "Not what we expected and the message is too long to fit ineeeeee one line" + assert { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9, + } == expected, ( + "Not what we expected and the message is too long to fit ineeeeee one line" + ) - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected - ), "Not what we expected and the message is too long to fit in one lineeeee" + assert { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9, + } == expected, ( + "Not what we expected and the message is too long to fit in one lineeeee" + ) - assert ( - { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - == expected - ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeee" + assert { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9, + } == expected, ( + "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeee" + ) assert ( { @@ -285,7 +282,9 @@ def test(): key9: value9, } == expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeee" + ), ( + "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeee" + ) assert expected == { key1: value1, @@ -299,20 +298,19 @@ def test(): key9: value9, }, "Not what we expected and the message is too long to fit ineeeeee one line" - assert ( - expected - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeeeeee" + assert expected == { + key1: value1, + key2: value2, + key3: value3, + key4: value4, + key5: value5, + key6: value6, + key7: value7, + key8: value8, + key9: value9, + }, ( + "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeeeeee" + ) assert ( expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee @@ -342,7 +340,9 @@ def test(): key8: value8, key9: value9, } - ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeee" + ), ( + "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeee" + ) # Test for https://github.com/astral-sh/ruff/issues/7246 @@ -361,156 +361,3 @@ assert package.files == [ }, ] ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -30,50 +30,47 @@ - - - def test(): -- assert ( -- { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } -- == expected -- ), "Not what we expected and the message is too long to fit ineeeeee one line" -+ assert { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } == expected, ( -+ "Not what we expected and the message is too long to fit ineeeeee one line" -+ ) - -- assert ( -- { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } -- == expected -- ), "Not what we expected and the message is too long to fit in one lineeeee" -+ assert { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } == expected, ( -+ "Not what we expected and the message is too long to fit in one lineeeee" -+ ) - -- assert ( -- { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } -- == expected -- ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeee" -+ assert { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ } == expected, ( -+ "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeee" -+ ) - - assert ( - { -@@ -103,7 +100,9 @@ - key9: value9, - } - == expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -- ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeee" -+ ), ( -+ "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeee" -+ ) - - assert expected == { - key1: value1, -@@ -117,20 +116,19 @@ - key9: value9, - }, "Not what we expected and the message is too long to fit ineeeeee one line" - -- assert ( -- expected -- == { -- key1: value1, -- key2: value2, -- key3: value3, -- key4: value4, -- key5: value5, -- key6: value6, -- key7: value7, -- key8: value8, -- key9: value9, -- } -- ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeeeeee" -+ assert expected == { -+ key1: value1, -+ key2: value2, -+ key3: value3, -+ key4: value4, -+ key5: value5, -+ key6: value6, -+ key7: value7, -+ key8: value8, -+ key9: value9, -+ }, ( -+ "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeeeeee" -+ ) - - assert ( - expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee -@@ -160,7 +158,9 @@ - key8: value8, - key9: value9, - } -- ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeee" -+ ), ( -+ "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeee" -+ ) - - - # Test for https://github.com/astral-sh/ruff/issues/7246 -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__assignment_split_value_first.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__assignment_split_value_first.py.snap index 746b781681..126735dac8 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__assignment_split_value_first.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__assignment_split_value_first.py.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/assignment_split_value_first.py -snapshot_kind: text --- ## Input ```python @@ -242,22 +241,7 @@ type A[VeryLongTypeNameThatShouldBreakFirstToTheRightBeforeSplitngtinthatExceeds ``` -## Outputs -### Output 1 -``` -indent-style = space -line-width = 88 -indent-width = 4 -quote-style = Double -line-ending = LineFeed -magic-trailing-comma = Respect -docstring-code = Disabled -docstring-code-line-width = "dynamic" -preview = Enabled -target_version = Py39 -source_type = Python -``` - +## Output ```python ####### # Unsplittable target and value diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap index 141e5590d6..852740fa6d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap @@ -695,7 +695,7 @@ match newlines: case "case comment with newlines" if foo == 2: # second pass - case "one", "newline" if (foo := 1): # third + case "one", "newline" if foo := 1: # third pass case "two newlines": @@ -708,7 +708,9 @@ match newlines: match long_lines: - case "this is a long line for if condition" if aaaaaaaaahhhhhhhh == 1 and bbbbbbaaaaaaaaaaa == 2: # comment + case "this is a long line for if condition" if ( + aaaaaaaaahhhhhhhh == 1 and bbbbbbaaaaaaaaaaa == 2 + ): # comment pass case "this is a long line for if condition with parentheses" if ( @@ -719,7 +721,7 @@ match long_lines: case "named expressions aren't special" if foo := 1: pass - case "named expressions aren't that special" if (foo := 1): + case "named expressions aren't that special" if foo := 1: pass case "but with already broken long lines" if ( @@ -727,9 +729,9 @@ match long_lines: ): # another comment pass - case { - "long_long_long_key": str(long_long_long_key) - } if value := "long long long long long long long long long long long value": + case {"long_long_long_key": str(long_long_long_key)} if ( + value := "long long long long long long long long long long long value" + ): pass @@ -824,7 +826,9 @@ match pattern_singleton: # trailing own 2 ): pass - case True: # trailing + case ( + True # trailing + ): pass case False: pass @@ -875,7 +879,9 @@ match foo: 1 ): y = 1 - case 1: # comment + case ( + 1 # comment + ): y = 1 case ( 1 @@ -1133,11 +1139,8 @@ match pattern_match_or: pass case ( - ( - a # trailing - ) - | (b) - ): + a # trailing + ) | (b): pass case a | b | c: @@ -1151,8 +1154,7 @@ match pattern_match_or: pass case ( # end of line - a - | b + a | b # own line ): pass @@ -1284,98 +1286,3 @@ match guard_comments: ): pass ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -69,7 +69,7 @@ - case "case comment with newlines" if foo == 2: # second - pass - -- case "one", "newline" if (foo := 1): # third -+ case "one", "newline" if foo := 1: # third - pass - - case "two newlines": -@@ -82,7 +82,9 @@ - - - match long_lines: -- case "this is a long line for if condition" if aaaaaaaaahhhhhhhh == 1 and bbbbbbaaaaaaaaaaa == 2: # comment -+ case "this is a long line for if condition" if ( -+ aaaaaaaaahhhhhhhh == 1 and bbbbbbaaaaaaaaaaa == 2 -+ ): # comment - pass - - case "this is a long line for if condition with parentheses" if ( -@@ -93,7 +95,7 @@ - case "named expressions aren't special" if foo := 1: - pass - -- case "named expressions aren't that special" if (foo := 1): -+ case "named expressions aren't that special" if foo := 1: - pass - - case "but with already broken long lines" if ( -@@ -101,9 +103,9 @@ - ): # another comment - pass - -- case { -- "long_long_long_key": str(long_long_long_key) -- } if value := "long long long long long long long long long long long value": -+ case {"long_long_long_key": str(long_long_long_key)} if ( -+ value := "long long long long long long long long long long long value" -+ ): - pass - - -@@ -198,7 +200,9 @@ - # trailing own 2 - ): - pass -- case True: # trailing -+ case ( -+ True # trailing -+ ): - pass - case False: - pass -@@ -249,7 +253,9 @@ - 1 - ): - y = 1 -- case 1: # comment -+ case ( -+ 1 # comment -+ ): - y = 1 - case ( - 1 -@@ -507,11 +513,8 @@ - pass - - case ( -- ( -- a # trailing -- ) -- | (b) -- ): -+ a # trailing -+ ) | (b): - pass - - case a | b | c: -@@ -525,8 +528,7 @@ - pass - - case ( # end of line -- a -- | b -+ a | b - # own line - ): - pass -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__return_annotation.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__return_annotation.py.snap index 4de17268e1..7bf5ec858f 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__return_annotation.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__return_annotation.py.snap @@ -336,32 +336,24 @@ def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]( # Breaking return type annotations. Black adds parentheses if the parameters are # empty; otherwise, it leverages the expressions own parentheses if possible. -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( - Set[ - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ] -): ... +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: ... -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( - Set[ - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ] -): ... +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: ... -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( - Set[ - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ] -): ... +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: ... -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( - Set[ - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - ] -): ... +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: ... def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( @@ -462,11 +454,8 @@ def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( ): ... -def double() -> ( - first_item - and foo.bar.baz().bop( - 1, - ) +def double() -> first_item and foo.bar.baz().bop( + 1, ): return 2 * a @@ -520,69 +509,3 @@ def process_board_action( ) -> Optional[Tuple[str, str]]: pass ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -131,32 +131,24 @@ - - # Breaking return type annotations. Black adds parentheses if the parameters are - # empty; otherwise, it leverages the expressions own parentheses if possible. --def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( -- Set[ -- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -- ] --): ... -+def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ -+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -+]: ... - - --def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( -- Set[ -- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -- ] --): ... -+def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ -+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -+]: ... - - --def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( -- Set[ -- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -- ] --): ... -+def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ -+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -+]: ... - - --def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( -- Set[ -- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -- ] --): ... -+def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ -+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -+]: ... - - - def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( -@@ -257,11 +249,8 @@ - ): ... - - --def double() -> ( -- first_item -- and foo.bar.baz().bop( -- 1, -- ) -+def double() -> first_item and foo.bar.baz().bop( -+ 1, - ): - return 2 * a - -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__return_type_no_parameters.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__return_type_no_parameters.py.snap index b02e9c8718..7c0350306c 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__return_type_no_parameters.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__return_type_no_parameters.py.snap @@ -245,11 +245,9 @@ def test_return_union_with_elements_exceeding_length() -> ( ######################################################################################### -def test_return_multiline_string_type_annotation() -> ( - """str +def test_return_multiline_string_type_annotation() -> """str | list[str] -""" -): +""": pass @@ -267,12 +265,12 @@ def test_return_multiline_string_binary_expression_return_type_annotation() -> ( ######################################################################################### -def test_implicit_concatenated_string_return_type() -> "str" "bbbbbbbbbbbbbbbb": +def test_implicit_concatenated_string_return_type() -> "strbbbbbbbbbbbbbbbb": pass def test_overlong_implicit_concatenated_string_return_type() -> ( - "liiiiiiiiiiiisssssst[str]" "bbbbbbbbbbbbbbbb" + "liiiiiiiiiiiisssssst[str]bbbbbbbbbbbbbbbb" ): pass @@ -295,9 +293,9 @@ def no_parameters_subscript_return_type() -> list[str]: # 1. Black tries to keep the list flat by parenthesizing the list as shown below even when the `list` identifier # fits on the header line. IMO, this adds unnecessary parentheses that can be avoided # and supporting it requires extra complexity (best_fitting! layout) -def no_parameters_overlong_subscript_return_type_with_single_element() -> ( - list[xxxxxxxxxxxxxxxxxxxxx] -): +def no_parameters_overlong_subscript_return_type_with_single_element() -> list[ + xxxxxxxxxxxxxxxxxxxxx +]: pass @@ -306,23 +304,18 @@ def no_parameters_overlong_subscript_return_type_with_single_element() -> ( # `list[`. It is also inconsistent with how subscripts are normally formatted where it first tries to fit the entire subscript, # then splits after `list[` but keeps all elements on a single line, and finally, splits after each element. # IMO: Splitting after the `list[` and trying to keep the elements together when possible seems more consistent. -def no_parameters_subscript_return_type_multiple_elements() -> ( - list[ - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, - ] -): +def no_parameters_subscript_return_type_multiple_elements() -> list[ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +]: pass # Black removes the parentheses even the elements exceed the configured line width. # So does Ruff. -def no_parameters_subscript_return_type_multiple_overlong_elements() -> ( - list[ - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, - ] -): +def no_parameters_subscript_return_type_multiple_overlong_elements() -> list[ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, +]: pass @@ -341,11 +334,9 @@ def no_parameters_subscriptreturn_type_with_overlong_value_() -> ( # `no_parameters_subscript_return_type_multiple_overlong_elements` shows. However, it doesn't # when the subscript contains a single element. Black then keeps the parentheses. # Ruff removes the parentheses in this case for consistency. -def no_parameters_overlong_subscript_return_type_with_overlong_single_element() -> ( - list[ - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - ] -): +def no_parameters_overlong_subscript_return_type_with_overlong_single_element() -> list[ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +]: pass @@ -354,13 +345,10 @@ def no_parameters_overlong_subscript_return_type_with_overlong_single_element() ######################################################################################### -def test_binary_expression_return_type_annotation() -> ( - aaaaaaaaaaaaaaaaaaaaaaaaaa - > [ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, - bbbbbbbbbbbbbbbbbbbbbbbbb, - ] -): +def test_binary_expression_return_type_annotation() -> aaaaaaaaaaaaaaaaaaaaaaaaaa > [ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbb, +]: pass @@ -370,139 +358,9 @@ def test_binary_expression_return_type_annotation() -> ( # Don't paranthesize lists -def f() -> ( - [ - a, - b, - ] -): +def f() -> [ + a, + b, +]: pass ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -58,11 +58,9 @@ - ######################################################################################### - - --def test_return_multiline_string_type_annotation() -> ( -- """str -+def test_return_multiline_string_type_annotation() -> """str - | list[str] --""" --): -+""": - pass - - -@@ -80,12 +78,12 @@ - ######################################################################################### - - --def test_implicit_concatenated_string_return_type() -> "str" "bbbbbbbbbbbbbbbb": -+def test_implicit_concatenated_string_return_type() -> "strbbbbbbbbbbbbbbbb": - pass - - - def test_overlong_implicit_concatenated_string_return_type() -> ( -- "liiiiiiiiiiiisssssst[str]" "bbbbbbbbbbbbbbbb" -+ "liiiiiiiiiiiisssssst[str]bbbbbbbbbbbbbbbb" - ): - pass - -@@ -108,9 +106,9 @@ - # 1. Black tries to keep the list flat by parenthesizing the list as shown below even when the `list` identifier - # fits on the header line. IMO, this adds unnecessary parentheses that can be avoided - # and supporting it requires extra complexity (best_fitting! layout) --def no_parameters_overlong_subscript_return_type_with_single_element() -> ( -- list[xxxxxxxxxxxxxxxxxxxxx] --): -+def no_parameters_overlong_subscript_return_type_with_single_element() -> list[ -+ xxxxxxxxxxxxxxxxxxxxx -+]: - pass - - -@@ -119,23 +117,18 @@ - # `list[`. It is also inconsistent with how subscripts are normally formatted where it first tries to fit the entire subscript, - # then splits after `list[` but keeps all elements on a single line, and finally, splits after each element. - # IMO: Splitting after the `list[` and trying to keep the elements together when possible seems more consistent. --def no_parameters_subscript_return_type_multiple_elements() -> ( -- list[ -- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, -- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, -- ] --): -+def no_parameters_subscript_return_type_multiple_elements() -> list[ -+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -+]: - pass - - - # Black removes the parentheses even the elements exceed the configured line width. - # So does Ruff. --def no_parameters_subscript_return_type_multiple_overlong_elements() -> ( -- list[ -- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, -- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, -- ] --): -+def no_parameters_subscript_return_type_multiple_overlong_elements() -> list[ -+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, -+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, -+]: - pass - - -@@ -154,11 +147,9 @@ - # `no_parameters_subscript_return_type_multiple_overlong_elements` shows. However, it doesn't - # when the subscript contains a single element. Black then keeps the parentheses. - # Ruff removes the parentheses in this case for consistency. --def no_parameters_overlong_subscript_return_type_with_overlong_single_element() -> ( -- list[ -- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -- ] --): -+def no_parameters_overlong_subscript_return_type_with_overlong_single_element() -> list[ -+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -+]: - pass - - -@@ -167,13 +158,10 @@ - ######################################################################################### - - --def test_binary_expression_return_type_annotation() -> ( -- aaaaaaaaaaaaaaaaaaaaaaaaaa -- > [ -- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, -- bbbbbbbbbbbbbbbbbbbbbbbbb, -- ] --): -+def test_binary_expression_return_type_annotation() -> aaaaaaaaaaaaaaaaaaaaaaaaaa > [ -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, -+ bbbbbbbbbbbbbbbbbbbbbbbbb, -+]: - pass - - -@@ -183,10 +171,8 @@ - - - # Don't paranthesize lists --def f() -> ( -- [ -- a, -- b, -- ] --): -+def f() -> [ -+ a, -+ b, -+]: - pass -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__return_type_parameters.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__return_type_parameters.py.snap index 1fc45eaee6..5f80f70e82 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__return_type_parameters.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__return_type_parameters.py.snap @@ -288,13 +288,13 @@ def test_return_multiline_string_binary_expression_return_type_annotation( ######################################################################################### -def test_implicit_concatenated_string_return_type(a) -> "str" "bbbbbbbbbbbbbbbb": +def test_implicit_concatenated_string_return_type(a) -> "strbbbbbbbbbbbbbbbb": pass def test_overlong_implicit_concatenated_string_return_type( a, -) -> "liiiiiiiiiiiisssssst[str]" "bbbbbbbbbbbbbbbb": +) -> "liiiiiiiiiiiisssssst[str]bbbbbbbbbbbbbbbb": pass @@ -413,26 +413,3 @@ def test_return_multiline_string_binary_expression_return_type_annotation( ]: pass ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -82,13 +82,13 @@ - ######################################################################################### - - --def test_implicit_concatenated_string_return_type(a) -> "str" "bbbbbbbbbbbbbbbb": -+def test_implicit_concatenated_string_return_type(a) -> "strbbbbbbbbbbbbbbbb": - pass - - - def test_overlong_implicit_concatenated_string_return_type( - a, --) -> "liiiiiiiiiiiisssssst[str]" "bbbbbbbbbbbbbbbb": -+) -> "liiiiiiiiiiiisssssst[str]bbbbbbbbbbbbbbbb": - pass - - -``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap index 42596504b2..3ee05f4763 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with.py -snapshot_kind: text --- ## Input ```python @@ -444,7 +443,9 @@ with ( with a: # should remove brackets pass -with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c: +with ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) as c: pass @@ -609,7 +610,9 @@ with ( # outer comment pass # Breaking of with items. -with test as ( # bar # foo +with ( + test # bar +) as ( # foo # test foo ): @@ -621,7 +624,9 @@ with test as ( ): pass -with test as ( # bar # foo # baz +with ( + test # bar +) as ( # foo # baz # test foo ): @@ -674,7 +679,9 @@ with ( ) as b: pass -with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b: +with ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) as b: pass with ( @@ -717,15 +724,19 @@ if True: pass if True: - with anyio.CancelScope( - shield=True - ) if get_running_loop() else contextlib.nullcontext(): + with ( + anyio.CancelScope(shield=True) + if get_running_loop() + else contextlib.nullcontext() + ): pass if True: - with anyio.CancelScope( - shield=True - ) if get_running_loop() else contextlib.nullcontext() as c: + with ( + anyio.CancelScope(shield=True) + if get_running_loop() + else contextlib.nullcontext() + ) as c: pass with Child(aaaaaaaaa, bbbbbbbbbbbbbbb, cccccc), Document( @@ -739,16 +750,22 @@ with open( ): pass -with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: +with ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +): pass -with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b: +with ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) as b: pass if True: - with anyio.CancelScope( - shield=True - ) if get_running_loop() else contextlib.nullcontext() as b: + with ( + anyio.CancelScope(shield=True) + if get_running_loop() + else contextlib.nullcontext() + ) as b: pass @@ -800,103 +817,7 @@ with ( ```diff --- Stable +++ Preview -@@ -49,7 +49,9 @@ - with a: # should remove brackets - pass - --with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c: -+with ( -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -+) as c: - pass - - -@@ -214,7 +216,9 @@ - pass - - # Breaking of with items. --with test as ( # bar # foo -+with ( -+ test # bar -+) as ( # foo - # test - foo - ): -@@ -226,7 +230,9 @@ - ): - pass - --with test as ( # bar # foo # baz -+with ( -+ test # bar -+) as ( # foo # baz - # test - foo - ): -@@ -279,7 +285,9 @@ - ) as b: - pass - --with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b: -+with ( -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -+) as b: - pass - - with ( -@@ -322,15 +330,19 @@ - pass - - if True: -- with anyio.CancelScope( -- shield=True -- ) if get_running_loop() else contextlib.nullcontext(): -+ with ( -+ anyio.CancelScope(shield=True) -+ if get_running_loop() -+ else contextlib.nullcontext() -+ ): - pass - - if True: -- with anyio.CancelScope( -- shield=True -- ) if get_running_loop() else contextlib.nullcontext() as c: -+ with ( -+ anyio.CancelScope(shield=True) -+ if get_running_loop() -+ else contextlib.nullcontext() -+ ) as c: - pass - - with Child(aaaaaaaaa, bbbbbbbbbbbbbbb, cccccc), Document( -@@ -344,57 +356,57 @@ - ): - pass - --with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: -+with ( -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -+): - pass - --with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b: -+with ( -+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -+) as b: - pass - - if True: -- with anyio.CancelScope( -- shield=True -- ) if get_running_loop() else contextlib.nullcontext() as b: -+ with ( -+ anyio.CancelScope(shield=True) -+ if get_running_loop() -+ else contextlib.nullcontext() -+ ) as b: - pass - +@@ -377,42 +377,36 @@ # Regression test for https://github.com/astral-sh/ruff/issues/14001 with (