Compare commits

...

3 Commits

Author SHA1 Message Date
Micha Reiser
d1a76d0011 POC of assert formatting 2023-09-19 09:48:31 +02:00
Micha Reiser
86bc5394f1 Discard changes to crates/ruff_python_formatter/src/lib.rs 2023-09-19 09:48:30 +02:00
Micha Reiser
eea082462c Potential fix for overlong parenthesized content 2023-09-19 08:42:00 +02:00
20 changed files with 494 additions and 396 deletions

View File

@@ -2155,15 +2155,18 @@ impl<Context> std::fmt::Debug for IndentIfGroupBreaks<'_, Context> {
}
}
/// Changes the definition of *fits* for `content`. Instead of measuring it in *flat*, measure it with
/// all line breaks expanded and test if no line exceeds the line width. The [`FitsExpanded`] acts
/// as a expands boundary similar to best fitting, meaning that a [`hard_line_break`] will not cause the parent group to expand.
/// Changes the definition of *fits* for `content`. It measures the width of all lines and allows
/// the content inside of the [`fits_expanded`] to exceed the configured line width. The content
/// coming before and after [`fits_expanded`] must fit into the configured line width.
///
/// The [`fits_expanded`] acts as a expands boundary similar to best fitting,
/// meaning that a [`hard_line_break`] will not cause the parent group to expand.
///
/// Useful in conjunction with a group with a condition.
///
/// ## Examples
/// The outer group with the binary expression remains *flat* regardless of the array expression
/// that spans multiple lines.
/// The outer group with the binary expression remains *flat* regardless of the array expression that
/// spans multiple lines with items exceeding the configured line width.
///
/// ```
/// # use ruff_formatter::{format, format_args, LineWidth, SimpleFormatOptions, write};
@@ -2183,7 +2186,7 @@ impl<Context> std::fmt::Debug for IndentIfGroupBreaks<'_, Context> {
/// token("["),
/// soft_block_indent(&format_args![
/// token("a,"), space(), token("# comment"), expand_parent(), soft_line_break_or_space(),
/// token("b")
/// token("'A very long string that exceeds the configured line width of 80 characters but the enclosing binary expression still fits.'")
/// ]),
/// token("]")
/// ]))
@@ -2194,7 +2197,7 @@ impl<Context> std::fmt::Debug for IndentIfGroupBreaks<'_, Context> {
/// let formatted = format!(SimpleFormatContext::default(), [content])?;
///
/// assert_eq!(
/// "a + [\n\ta, # comment\n\tb\n]",
/// "a + [\n\ta, # comment\n\t'A very long string that exceeds the configured line width of 80 characters but the enclosing binary expression still fits.'\n]",
/// formatted.print()?.as_code()
/// );
/// # Ok(())

View File

@@ -11,7 +11,7 @@ use unicode_width::UnicodeWidthChar;
use crate::format_element::tag::{GroupMode, LabelId, Tag};
use crate::source_code::SourceCodeSlice;
use crate::{IndentWidth, TagKind};
use crate::{GroupId, IndentWidth, TagKind};
use ruff_text_size::TextSize;
/// Language agnostic IR for formatting source code.
@@ -34,7 +34,9 @@ pub enum FormatElement {
SourcePosition(TextSize),
/// A ASCII only Token that contains no line breaks or tab characters.
Token { text: &'static str },
Token {
text: &'static str,
},
/// An arbitrary text that can contain tabs, newlines, and unicode characters.
Text {
@@ -57,6 +59,11 @@ pub enum FormatElement {
/// deep cloning the IR when using the `best_fitting!` macro or `if_group_fits_on_line` and `if_group_breaks`.
Interned(Interned),
GroupMode {
id: GroupId,
mode: PrintMode,
},
/// A list of different variants representing the same content. The printer picks the best fitting content.
/// Line breaks inside of a best fitting don't propagate to parent groups.
BestFitting {
@@ -97,6 +104,9 @@ impl std::fmt::Debug for FormatElement {
.field("variants", variants)
.field("mode", &mode)
.finish(),
FormatElement::GroupMode { id, mode } => {
fmt.debug_tuple("GroupMode").field(id).field(mode).finish()
}
FormatElement::Interned(interned) => fmt.debug_list().entries(&**interned).finish(),
FormatElement::Tag(tag) => fmt.debug_tuple("Tag").field(tag).finish(),
FormatElement::SourcePosition(position) => {
@@ -277,6 +287,7 @@ impl FormatElements for FormatElement {
FormatElement::LineSuffixBoundary
| FormatElement::Space
| FormatElement::Tag(_)
| FormatElement::GroupMode { .. }
| FormatElement::Token { .. }
| FormatElement::SourcePosition(_) => false,
}

View File

@@ -381,6 +381,17 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
write!(f, [token("])")])?;
}
FormatElement::GroupMode { id, mode } => write!(
f,
[
token("group_mode("),
text(&std::format!("{id:?}"), None),
token(", "),
text(&std::format!("{mode:?}"), None),
token(")")
]
)?,
FormatElement::Interned(interned) => {
let interned_elements = &mut f.context_mut().printed_interned_elements;

View File

@@ -84,11 +84,14 @@ pub enum Tag {
StartFitsExpanded(FitsExpanded),
EndFitsExpanded,
/// Marks the start and end of a best-fitting variant.
StartBestFittingEntry,
EndBestFittingEntry,
/// Parenthesizes the content but only if adding the parentheses and indenting the content
/// makes the content fit in the configured line width.
///
/// See [`crate::builders::best_fit_parenthesize`] for an in-depth explanation.
StartBestFitParenthesize {
id: Option<GroupId>,
},

View File

@@ -1,5 +1,3 @@
#![allow(dead_code)]
use crate::prelude::*;
use std::cell::OnceCell;
use std::marker::PhantomData;

View File

@@ -141,6 +141,10 @@ impl<'a> Printer<'a> {
// Handled in `Document::propagate_expands()
}
FormatElement::GroupMode { id, mode } => {
self.state.group_modes.insert_print_mode(*id, *mode);
}
FormatElement::SourcePosition(position) => {
self.state.source_position = *position;
self.push_marker();
@@ -1183,7 +1187,7 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
// line break should be printed as regular line break
return Ok(Fits::Yes);
}
MeasureMode::AllLines => {
MeasureMode::AllLines | MeasureMode::AllLinesAllowTextOverflow => {
// Continue measuring on the next line
self.state.line_width = 0;
self.state.pending_indent = args.indention();
@@ -1219,6 +1223,10 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
}
}
FormatElement::GroupMode { id, mode } => {
self.group_modes_mut().insert_print_mode(*id, *mode);
}
FormatElement::ExpandParent => {
if self.must_be_flat {
return Ok(Fits::No);
@@ -1354,9 +1362,11 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
}
FormatElement::Tag(StartLineSuffix { reserved_width }) => {
self.state.line_width += reserved_width;
if self.state.line_width > self.options().line_width.into() {
return Ok(Fits::No);
if *reserved_width > 0 {
self.state.line_width += reserved_width;
if self.state.line_width > self.options().line_width.into() {
return Ok(Fits::No);
}
}
self.queue.skip_content(TagKind::LineSuffix);
self.state.has_line_suffix = true;
@@ -1370,32 +1380,42 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
condition,
propagate_expand,
})) => {
let condition_met = match condition {
Some(condition) => {
let group_mode = match condition.group_id {
Some(group_id) => self.group_modes().get_print_mode(group_id)?,
None => args.mode(),
match args.mode() {
PrintMode::Expanded => {
// As usual, nothing to measure
self.stack.push(TagKind::FitsExpanded, args);
}
PrintMode::Flat => {
let condition_met = match condition {
Some(condition) => {
let group_mode = match condition.group_id {
Some(group_id) => {
self.group_modes().get_print_mode(group_id)?
}
None => args.mode(),
};
condition.mode == group_mode
}
None => true,
};
condition.mode == group_mode
}
None => true,
};
if condition_met {
// Measure in fully expanded mode and allow overflows
self.stack.push(
TagKind::FitsExpanded,
args.with_measure_mode(MeasureMode::AllLinesAllowTextOverflow)
.with_print_mode(PrintMode::Expanded),
);
} else {
if propagate_expand.get() {
return Ok(Fits::No);
}
if condition_met {
// Measure in fully expanded mode.
self.stack.push(
TagKind::FitsExpanded,
args.with_print_mode(PrintMode::Expanded)
.with_measure_mode(MeasureMode::AllLines),
);
} else {
if propagate_expand.get() && args.mode().is_flat() {
return Ok(Fits::No);
// As usual
self.stack.push(TagKind::FitsExpanded, args);
}
}
// As usual
self.stack.push(TagKind::FitsExpanded, args);
}
}
@@ -1482,7 +1502,8 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
}
match args.measure_mode() {
MeasureMode::FirstLine => return Fits::Yes,
MeasureMode::AllLines => {
MeasureMode::AllLines
| MeasureMode::AllLinesAllowTextOverflow => {
self.state.line_width = 0;
continue;
}
@@ -1498,7 +1519,9 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
}
}
if self.state.line_width > self.options().line_width.into() {
if self.state.line_width > self.options().line_width.into()
&& !args.measure_mode().allows_text_overflow()
{
return Fits::No;
}
@@ -1601,6 +1624,17 @@ enum MeasureMode {
/// The content only fits if none of the lines exceed the print width. Lines are terminated by either
/// a hard line break or a soft line break in [`PrintMode::Expanded`].
AllLines,
/// Measures all lines and allows lines to exceed the configured line width. Useful when it only matters
/// whether the content *before* and *after* fits.
AllLinesAllowTextOverflow,
}
impl MeasureMode {
/// Returns `true` if this mode allows text exceeding the configured line width.
const fn allows_text_overflow(self) -> bool {
matches!(self, MeasureMode::AllLinesAllowTextOverflow)
}
}
impl From<BestFittingMode> for MeasureMode {

View File

@@ -120,7 +120,6 @@ impl SourceMapGeneration {
}
}
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum LineEnding {

View File

@@ -394,3 +394,15 @@ z = (
# c: and this comment
+ a
)
# Test for https://github.com/astral-sh/ruff/issues/7431
if True:
if True:
if True:
if True:
msg += " " + _(
"Since the role is not mentionable, it will be momentarily made mentionable "
"when announcing a streamalert. Please make sure I have the correct "
"permissions to manage this role, or else members of this role won't receive "
"a notification."
)

View File

@@ -94,8 +94,3 @@ def f():
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = (
True
)
# Regression test for https://github.com/astral-sh/ruff/issues/7462
if grid is not None:
rgrid = (rgrid.rio.reproject_match(grid, nodata=fillvalue) # rio.reproject nodata is use to initlialize the destination array
.where(~grid.isnull()))

View File

@@ -153,3 +153,19 @@ def test():
key9: value9,
}
), "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
assert items == [
"a very very very very very very very very very very very very very very very long string",
]
assert package.files == [
{
"file": "pytest-3.5.0-py2.py3-none-any.whl",
"hash": "sha256:6266f87ab64692112e5477eba395cfedda53b1933ccd29478e671e73b420c19c", # noqa: E501
},
{
"file": "pytest-3.5.0.tar.gz",
"hash": "sha256:fae491d1874f199537fd5872b5e1f0e74a009b979df9d53d1553fd03da1703e1", # noqa: E501
},
]

View File

@@ -1,4 +1,4 @@
use ruff_formatter::{format_args, write, Argument, Arguments};
use ruff_formatter::{format_args, write, Argument, Arguments, GroupId};
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::context::{NodeLevel, WithNodeLevel};
@@ -12,11 +12,28 @@ where
{
ParenthesizeIfExpands {
inner: Argument::new(content),
group_id: None,
should_expand: false,
}
}
pub(crate) struct ParenthesizeIfExpands<'a, 'ast> {
inner: Argument<'a, PyFormatContext<'ast>>,
group_id: Option<GroupId>,
should_expand: bool,
}
impl ParenthesizeIfExpands<'_, '_> {
#[must_use]
pub(crate) fn should_expand(mut self, expand: bool) -> Self {
self.should_expand = expand;
self
}
pub(crate) fn with_group_id(mut self, group_id: GroupId) -> Self {
self.group_id = Some(group_id);
self
}
}
impl<'ast> Format<PyFormatContext<'ast>> for ParenthesizeIfExpands<'_, 'ast> {
@@ -30,7 +47,9 @@ impl<'ast> Format<PyFormatContext<'ast>> for ParenthesizeIfExpands<'_, 'ast> {
if_group_breaks(&token("(")),
soft_block_indent(&Arguments::from(&self.inner)),
if_group_breaks(&token(")")),
])]
])
.with_group_id(self.group_id)
.should_expand(self.should_expand)]
)
}
}

View File

@@ -12,7 +12,7 @@ use ruff_python_ast::{Constant, Expr, ExpressionRef, Operator};
use ruff_python_trivia::CommentRanges;
use crate::builders::parenthesize_if_expands;
use crate::comments::leading_comments;
use crate::comments::{leading_comments, LeadingDanglingTrailingComments};
use crate::context::{NodeLevel, WithNodeLevel};
use crate::expression::parentheses::{
is_expression_parenthesized, optional_parentheses, parenthesized, NeedsParentheses,
@@ -179,6 +179,45 @@ pub(crate) struct MaybeParenthesizeExpression<'a> {
parenthesize: Parenthesize,
}
impl MaybeParenthesizeExpression<'_> {
pub(crate) fn needs_parentheses(
&self,
context: &PyFormatContext,
node_comments: &LeadingDanglingTrailingComments,
) -> OptionalParentheses {
let MaybeParenthesizeExpression {
expression,
parent,
parenthesize,
} = self;
let preserve_parentheses = parenthesize.is_optional()
&& is_expression_parenthesized(
(*expression).into(),
context.comments().ranges(),
context.source(),
);
let has_comments = node_comments.has_leading() || node_comments.has_trailing_own_line();
// If the expression has comments, we always want to preserve the parentheses. This also
// ensures that we correctly handle parenthesized comments, and don't need to worry about
// them in the implementation below.
if preserve_parentheses || has_comments {
OptionalParentheses::Always
} else {
match expression.needs_parentheses(*parent, context) {
OptionalParentheses::Always => OptionalParentheses::Always,
// The reason to add parentheses is to avoid a syntax error when breaking an expression over multiple lines.
// Therefore, it is unnecessary to add an additional pair of parentheses if an outer expression
// is parenthesized.
_ if context.node_level().is_parenthesized() => OptionalParentheses::Never,
needs_parentheses => needs_parentheses,
}
}
}
}
impl Format<PyFormatContext<'_>> for MaybeParenthesizeExpression<'_> {
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
let MaybeParenthesizeExpression {
@@ -188,34 +227,9 @@ impl Format<PyFormatContext<'_>> for MaybeParenthesizeExpression<'_> {
} = self;
let comments = f.context().comments();
let preserve_parentheses = parenthesize.is_optional()
&& is_expression_parenthesized(
(*expression).into(),
f.context().comments().ranges(),
f.context().source(),
);
let node_comments = comments.leading_dangling_trailing(*expression);
let has_comments = node_comments.has_leading() || node_comments.has_trailing_own_line();
// If the expression has comments, we always want to preserve the parentheses. This also
// ensures that we correctly handle parenthesized comments, and don't need to worry about
// them in the implementation below.
if preserve_parentheses || has_comments {
return expression.format().with_options(Parentheses::Always).fmt(f);
}
let needs_parentheses = match expression.needs_parentheses(*parent, f.context()) {
OptionalParentheses::Always => OptionalParentheses::Always,
// The reason to add parentheses is to avoid a syntax error when breaking an expression over multiple lines.
// Therefore, it is unnecessary to add an additional pair of parentheses if an outer expression
// is parenthesized.
_ if f.context().node_level().is_parenthesized() => OptionalParentheses::Never,
needs_parentheses => needs_parentheses,
};
match needs_parentheses {
match self.needs_parentheses(f.context(), &node_comments) {
OptionalParentheses::Multiline => match parenthesize {
Parenthesize::IfBreaksOrIfRequired => {
parenthesize_if_expands(&expression.format().with_options(Parentheses::Never))
@@ -342,7 +356,7 @@ impl<'ast> IntoFormat<PyFormatContext<'ast>> for Expr {
/// * The expression contains at least one parenthesized sub expression (optimization to avoid unnecessary work)
///
/// This mimics Black's [`_maybe_split_omitting_optional_parens`](https://github.com/psf/black/blob/d1248ca9beaf0ba526d265f4108836d89cf551b7/src/black/linegen.py#L746-L820)
fn can_omit_optional_parentheses(expr: &Expr, context: &PyFormatContext) -> bool {
pub(crate) fn can_omit_optional_parentheses(expr: &Expr, context: &PyFormatContext) -> bool {
let mut visitor = CanOmitOptionalParenthesesVisitor::new(context);
visitor.visit_subexpression(expr);

View File

@@ -1,5 +1,5 @@
use ruff_formatter::prelude::tag::Condition;
use ruff_formatter::{format_args, write, Argument, Arguments};
use ruff_formatter::{format_args, write, Argument, Arguments, GroupId};
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::ExpressionRef;
use ruff_python_trivia::CommentRanges;
@@ -155,32 +155,34 @@ impl<'content, 'ast> FormatParenthesized<'content, 'ast> {
impl<'ast> Format<PyFormatContext<'ast>> for FormatParenthesized<'_, 'ast> {
fn fmt(&self, f: &mut Formatter<PyFormatContext<'ast>>) -> FormatResult<()> {
let inner = format_with(|f| {
let current_level = f.context().node_level();
let content = format_with(|f| {
group(&format_args![
token(self.left),
dangling_open_parenthesis_comments(self.comments),
soft_block_indent(&Arguments::from(&self.content)),
token(self.right)
soft_block_indent(&Arguments::from(&self.content))
])
.fmt(f)
});
let current_level = f.context().node_level();
let inner = format_with(|f| {
if let NodeLevel::Expression(Some(group_id)) = current_level {
// Use fits expanded if there's an enclosing group that adds the optional parentheses.
// This ensures that expanding this parenthesized expression does not expand the optional parentheses group.
write!(
f,
[fits_expanded(&content)
.with_condition(Some(Condition::if_group_fits_on_line(group_id)))]
)
} else {
// It's not necessary to wrap the content if it is not inside of an optional_parentheses group.
content.fmt(f)
}
});
let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);
if let NodeLevel::Expression(Some(group_id)) = current_level {
// Use fits expanded if there's an enclosing group that adds the optional parentheses.
// This ensures that expanding this parenthesized expression does not expand the optional parentheses group.
write!(
f,
[fits_expanded(&inner)
.with_condition(Some(Condition::if_group_fits_on_line(group_id)))]
)
} else {
// It's not necessary to wrap the content if it is not inside of an optional_parentheses group.
write!(f, [inner])
}
write!(f, [token(self.left), inner, token(self.right)])
}
}
@@ -194,18 +196,29 @@ where
{
FormatOptionalParentheses {
content: Argument::new(content),
group_id: None,
}
}
pub(crate) struct FormatOptionalParentheses<'content, 'ast> {
content: Argument<'content, PyFormatContext<'ast>>,
group_id: Option<GroupId>,
}
impl FormatOptionalParentheses<'_, '_> {
pub(crate) fn with_group_id(mut self, group_id: GroupId) -> Self {
self.group_id = Some(group_id);
self
}
}
impl<'ast> Format<PyFormatContext<'ast>> for FormatOptionalParentheses<'_, 'ast> {
fn fmt(&self, f: &mut Formatter<PyFormatContext<'ast>>) -> FormatResult<()> {
// The group id is used as a condition in [`in_parentheses_only_group`] to create a
// conditional group that is only active if the optional parentheses group expands.
let parens_id = f.group_id("optional_parentheses");
let parens_id = self
.group_id
.unwrap_or_else(|| f.group_id("optional_parentheses"));
let mut f = WithNodeLevel::new(NodeLevel::Expression(Some(parens_id)), f);

View File

@@ -1,12 +1,13 @@
use ruff_formatter::prelude::{space, token};
use ruff_formatter::write;
use ruff_python_ast::StmtAssert;
use ruff_formatter::{format_args, write};
use ruff_python_ast::{Constant, Expr, ExprConstant, StmtAssert};
use crate::builders::parenthesize_if_expands;
use crate::comments::{SourceComment, SuppressionKind};
use crate::expression::maybe_parenthesize_expression;
use crate::expression::parentheses::Parenthesize;
use crate::context::{NodeLevel, WithNodeLevel};
use crate::expression::parentheses::{OptionalParentheses, Parentheses, Parenthesize};
use crate::expression::{can_omit_optional_parentheses, maybe_parenthesize_expression};
use crate::prelude::*;
use crate::FormatNodeRule;
#[derive(Default)]
pub struct FormatStmtAssert;
@@ -19,14 +20,102 @@ impl FormatNodeRule<StmtAssert> for FormatStmtAssert {
msg,
} = item;
write!(
f,
[
token("assert"),
space(),
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks)
]
)?;
write!(f, [token("assert"), space()])?;
let parenthesize_test = maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks);
if let Some(
msg @ (Expr::FString(_)
| Expr::Constant(ExprConstant {
value: Constant::Str(_) | Constant::Bytes(_),
..
})),
) = msg.as_deref()
{
let parenthesize_message =
maybe_parenthesize_expression(msg, item, Parenthesize::IfBreaks);
let comments = f.context().comments();
let test_comments = comments.leading_dangling_trailing(test.as_ref());
let msg_comments = comments.leading_dangling_trailing(msg);
// TODO limit to can omit parentheses and has own parentheses
if parenthesize_test.needs_parentheses(f.context(), &test_comments)
== OptionalParentheses::Multiline
&& parenthesize_message.needs_parentheses(f.context(), &msg_comments)
== OptionalParentheses::BestFit
&& can_omit_optional_parentheses(test, f.context())
{
let test_group_id = f.group_id("optional_parentheses");
let mut format_test = test.format().with_options(Parentheses::Never).memoized();
let mut format_msg = msg.format().with_options(Parentheses::Never).memoized();
let test_breaks = {
let f = &mut WithNodeLevel::new(NodeLevel::Expression(Some(test_group_id)), f);
format_test.inspect(f)?.will_break()
};
return if test_breaks || format_msg.inspect(f)?.will_break() {
todo!()
} else {
best_fitting![
// ---------------------------------------------------------------------
// Variant 1:
// Try to fit both expressions without parentheses
format_args![
group(&format_test).with_group_id(Some(test_group_id)),
token(","),
space(),
format_msg
],
// ---------------------------------------------------------------------
// Variant 2:
// Try to parenthesize the string, but don't parenthesize the test just yet
format_args![
group(&format_test).with_group_id(Some(test_group_id)),
token(","),
space(),
parenthesize_if_expands(&format_msg).should_expand(true)
],
// ---------------------------------------------------------------------
// Variant 3:
// Try to parenthesize both test and message
format_args![
parenthesize_if_expands(&format_test)
.with_group_id(test_group_id)
.should_expand(true),
token(","),
space(),
parenthesize_if_expands(&format_msg).should_expand(true)
],
// ---------------------------------------------------------------------
// Variant 4:
// If it wasn't possible to make test and message fit by now, fallback to the first variant
// and omit any optional parentheses.
format_args![
// Create an empty group that always fits. Necessary because the content
// of format_test might refer the group id.
format_with(|f| {
f.write_element(FormatElement::GroupMode {
id: test_group_id,
mode: PrintMode::Flat,
});
Ok(())
}),
format_test,
token(","),
space(),
format_msg
],
]
.with_mode(BestFittingMode::AllLines)
.fmt(f)
};
}
}
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks).fmt(f)?;
if let Some(msg) = msg {
write!(

View File

@@ -193,41 +193,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
@@ -355,20 +321,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

View File

@@ -193,41 +193,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
@@ -355,20 +321,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

View File

@@ -1,77 +0,0 @@
---
source: crates/ruff_python_formatter/tests/fixtures.rs
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens3.py
---
## Input
```py
if True:
if True:
if True:
return _(
"qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas "
+ "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.",
"qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe",
) % {"reported_username": reported_username, "report_reason": report_reason}
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -1,8 +1,14 @@
if True:
if True:
if True:
- return _(
- "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas "
- + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.",
- "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe",
- ) % {"reported_username": reported_username, "report_reason": report_reason}
+ return (
+ _(
+ "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas "
+ + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.",
+ "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe",
+ )
+ % {
+ "reported_username": reported_username,
+ "report_reason": report_reason,
+ }
+ )
```
## Ruff Output
```py
if True:
if True:
if True:
return (
_(
"qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas "
+ "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.",
"qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe",
)
% {
"reported_username": reported_username,
"report_reason": report_reason,
}
)
```
## Black Output
```py
if True:
if True:
if True:
return _(
"qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas "
+ "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.",
"qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe",
) % {"reported_username": reported_username, "report_reason": report_reason}
```

View File

@@ -400,6 +400,18 @@ z = (
# c: and this comment
+ a
)
# Test for https://github.com/astral-sh/ruff/issues/7431
if True:
if True:
if True:
if True:
msg += " " + _(
"Since the role is not mentionable, it will be momentarily made mentionable "
"when announcing a streamalert. Please make sure I have the correct "
"permissions to manage this role, or else members of this role won't receive "
"a notification."
)
```
## Output
@@ -849,6 +861,18 @@ z = (
# c: and this comment
+ a
)
# Test for https://github.com/astral-sh/ruff/issues/7431
if True:
if True:
if True:
if True:
msg += " " + _(
"Since the role is not mentionable, it will be momentarily made mentionable "
"when announcing a streamalert. Please make sure I have the correct "
"permissions to manage this role, or else members of this role won't receive "
"a notification."
)
```

View File

@@ -100,11 +100,6 @@ def f():
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = (
True
)
# Regression test for https://github.com/astral-sh/ruff/issues/7462
if grid is not None:
rgrid = (rgrid.rio.reproject_match(grid, nodata=fillvalue) # rio.reproject nodata is use to initlialize the destination array
.where(~grid.isnull()))
```
## Output
@@ -227,15 +222,6 @@ def f():
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = (
True
)
# Regression test for https://github.com/astral-sh/ruff/issues/7462
if grid is not None:
rgrid = rgrid.rio.reproject_match(
grid, nodata=fillvalue
).where( # rio.reproject nodata is use to initlialize the destination array
~grid.isnull()
)
```

View File

@@ -159,6 +159,22 @@ def test():
key9: value9,
}
), "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
assert items == [
"a very very very very very very very very very very very very very very very long string",
]
assert package.files == [
{
"file": "pytest-3.5.0-py2.py3-none-any.whl",
"hash": "sha256:6266f87ab64692112e5477eba395cfedda53b1933ccd29478e671e73b420c19c", # noqa: E501
},
{
"file": "pytest-3.5.0.tar.gz",
"hash": "sha256:fae491d1874f199537fd5872b5e1f0e74a009b979df9d53d1553fd03da1703e1", # noqa: E501
},
]
```
## Output
@@ -195,80 +211,69 @@ 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 (
{
key1: value1,
key2: value2,
key3: value3,
key4: value4,
key5: value5,
key6: value6,
key7: value7,
key8: value8,
key9: value9,
}
== expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
), "Not what we expected and the message is too long to fit in one lin"
assert {
key1: value1,
key2: value2,
key3: value3,
key4: value4,
key5: value5,
key6: value6,
key7: value7,
key8: value8,
key9: value9,
} == expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee, "Not what we expected and the message is too long to fit in one lin"
assert (
{
key1: value1,
key2: value2,
key3: value3,
key4: value4,
key5: value5,
key6: value6,
key7: value7,
key8: value8,
key9: value9,
}
== expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeee"
assert {
key1: value1,
key2: value2,
key3: value3,
key4: value4,
key5: value5,
key6: value6,
key7: value7,
key8: value8,
key9: value9,
} == expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee, "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeee"
assert expected == {
key1: value1,
@@ -282,20 +287,17 @@ 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
@@ -310,22 +312,38 @@ def test():
key8: value8,
key9: value9,
}
), "Not what we expected and the message is too long to fit in one lin"
), (
"Not what we expected and the message is too long to fit in one lin"
)
assert (
expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
== {
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 lineeeeeeeeeeeeeee"
assert expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee == {
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 lineeeeeeeeeeeeeee"
# Test for https://github.com/astral-sh/ruff/issues/7246
assert items == [
"a very very very very very very very very very very very very very very very long string",
]
assert package.files == [
{
"file": "pytest-3.5.0-py2.py3-none-any.whl",
"hash": "sha256:6266f87ab64692112e5477eba395cfedda53b1933ccd29478e671e73b420c19c", # noqa: E501
},
{
"file": "pytest-3.5.0.tar.gz",
"hash": "sha256:fae491d1874f199537fd5872b5e1f0e74a009b979df9d53d1553fd03da1703e1", # noqa: E501
},
]
```