mirror of
https://github.com/astral-sh/ruff
synced 2026-01-22 05:51:03 -05:00
Keep lambda parameters on one line and parenthesize the body if it expands (#21385)
## Summary This PR makes two changes to our formatting of `lambda` expressions: 1. We now parenthesize the body expression if it expands 2. We now try to keep the parameters on a single line The latter of these fixes #8179: Black formatting and this PR's formatting: ```py def a(): return b( c, d, e, f=lambda self, *args, **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( *args, **kwargs ), ) ``` Stable Ruff formatting ```py def a(): return b( c, d, e, f=lambda self, *args, **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs), ) ``` We don't parenthesize the body expression here because the call to `aaaa...` has its own parentheses, but adding a binary operator shows the new parenthesization: ```diff @@ -3,7 +3,7 @@ c, d, e, - f=lambda self, *args, **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( - *args, **kwargs - ) + 1, + f=lambda self, *args, **kwargs: ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs) + 1 + ), ) ``` This is actually a new divergence from Black, which formats this input like this: ```py def a(): return b( c, d, e, f=lambda self, *args, **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( *args, **kwargs ) + 1, ) ``` But I think this is an improvement, unlike the case from #8179. One other, smaller benefit is that because we now add parentheses to lambda bodies, we also remove redundant parentheses: ```diff @pytest.mark.parametrize( "f", [ - lambda x: (x.expanding(min_periods=5).cov(x, pairwise=True)), - lambda x: (x.expanding(min_periods=5).corr(x, pairwise=True)), + lambda x: x.expanding(min_periods=5).cov(x, pairwise=True), + lambda x: x.expanding(min_periods=5).corr(x, pairwise=True), ], ) def test_moment_functions_zero_length_pairwise(f): ``` ## Test Plan New tests taken from #8465 and probably a few more I should grab from the ecosystem results. --------- Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
@@ -9,6 +9,7 @@ use crate::comments::{
|
||||
Comments, LeadingDanglingTrailingComments, SourceComment, trailing_comments,
|
||||
};
|
||||
use crate::context::{NodeLevel, WithNodeLevel};
|
||||
use crate::expression::expr_lambda::ExprLambdaLayout;
|
||||
use crate::expression::parentheses::{
|
||||
NeedsParentheses, OptionalParentheses, Parentheses, Parenthesize, is_expression_parenthesized,
|
||||
optional_parentheses,
|
||||
@@ -18,6 +19,7 @@ use crate::expression::{
|
||||
maybe_parenthesize_expression,
|
||||
};
|
||||
use crate::other::interpolated_string::InterpolatedStringLayout;
|
||||
use crate::preview::is_parenthesize_lambda_bodies_enabled;
|
||||
use crate::statement::trailing_semicolon;
|
||||
use crate::string::StringLikeExtensions;
|
||||
use crate::string::implicit::{
|
||||
@@ -303,12 +305,7 @@ impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
|
||||
&& format_implicit_flat.is_none()
|
||||
&& format_interpolated_string.is_none()
|
||||
{
|
||||
return maybe_parenthesize_expression(
|
||||
value,
|
||||
*statement,
|
||||
Parenthesize::IfBreaks,
|
||||
)
|
||||
.fmt(f);
|
||||
return maybe_parenthesize_value(value, *statement).fmt(f);
|
||||
}
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
@@ -586,11 +583,7 @@ impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
|
||||
space(),
|
||||
operator,
|
||||
space(),
|
||||
maybe_parenthesize_expression(
|
||||
value,
|
||||
*statement,
|
||||
Parenthesize::IfBreaks
|
||||
)
|
||||
maybe_parenthesize_value(value, *statement)
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -1369,3 +1362,32 @@ fn is_attribute_with_parenthesized_value(target: &Expr, context: &PyFormatContex
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`maybe_parenthesize_expression`] but with special handling for lambdas in preview.
|
||||
fn maybe_parenthesize_value<'a>(
|
||||
expression: &'a Expr,
|
||||
parent: AnyNodeRef<'a>,
|
||||
) -> MaybeParenthesizeValue<'a> {
|
||||
MaybeParenthesizeValue { expression, parent }
|
||||
}
|
||||
|
||||
struct MaybeParenthesizeValue<'a> {
|
||||
expression: &'a Expr,
|
||||
parent: AnyNodeRef<'a>,
|
||||
}
|
||||
|
||||
impl Format<PyFormatContext<'_>> for MaybeParenthesizeValue<'_> {
|
||||
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
let MaybeParenthesizeValue { expression, parent } = self;
|
||||
|
||||
if is_parenthesize_lambda_bodies_enabled(f.context())
|
||||
&& let Expr::Lambda(lambda) = expression
|
||||
&& !f.context().comments().has_leading(lambda)
|
||||
{
|
||||
parenthesize_if_expands(&lambda.format().with_options(ExprLambdaLayout::Assignment))
|
||||
.fmt(f)
|
||||
} else {
|
||||
maybe_parenthesize_expression(expression, *parent, Parenthesize::IfBreaks).fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user