mirror of https://github.com/astral-sh/ruff
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:
parent
d5546508cf
commit
0ebdebddd8
|
|
@ -125,6 +125,13 @@ lambda a, /, c: a
|
|||
*x: x
|
||||
)
|
||||
|
||||
(
|
||||
lambda
|
||||
# comment
|
||||
*x,
|
||||
**y: x
|
||||
)
|
||||
|
||||
(
|
||||
lambda
|
||||
# comment 1
|
||||
|
|
@ -196,6 +203,17 @@ lambda: ( # comment
|
|||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda # 1
|
||||
# 2
|
||||
x, # 3
|
||||
# 4
|
||||
y
|
||||
: # 5
|
||||
# 6
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda
|
||||
x,
|
||||
|
|
@ -204,6 +222,71 @@ lambda: ( # comment
|
|||
z
|
||||
)
|
||||
|
||||
|
||||
# Leading
|
||||
lambda x: (
|
||||
lambda y: lambda z: x
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ y
|
||||
+ z # Trailing
|
||||
) # Trailing
|
||||
|
||||
|
||||
# Leading
|
||||
lambda x: lambda y: lambda z: [
|
||||
x,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
y,
|
||||
z
|
||||
] # Trailing
|
||||
# Trailing
|
||||
|
||||
lambda self, araa, kkkwargs=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs), e=1, f=2, g=2: d
|
||||
|
||||
# Regression tests for https://github.com/astral-sh/ruff/issues/8179
|
||||
|
|
@ -228,6 +311,441 @@ def a():
|
|||
g = 10
|
||||
)
|
||||
|
||||
def a():
|
||||
return b(
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
f=lambda self, *args, **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
|
||||
*args, **kwargs
|
||||
) + 1,
|
||||
)
|
||||
|
||||
# Additional ecosystem cases from https://github.com/astral-sh/ruff/pull/21385
|
||||
class C:
|
||||
def foo():
|
||||
mock_service.return_value.bucket.side_effect = lambda name: (
|
||||
source_bucket
|
||||
if name == source_bucket_name
|
||||
else storage.Bucket(mock_service, destination_bucket_name)
|
||||
)
|
||||
|
||||
class C:
|
||||
function_dict: Dict[Text, Callable[[CRFToken], Any]] = {
|
||||
CRFEntityExtractorOptions.POS2: lambda crf_token: crf_token.pos_tag[:2]
|
||||
if crf_token.pos_tag is not None
|
||||
else None,
|
||||
}
|
||||
|
||||
name = re.sub(r"[^\x21\x23-\x5b\x5d-\x7e]...............", lambda m: f"\\{m.group(0)}", p["name"])
|
||||
|
||||
def foo():
|
||||
if True:
|
||||
if True:
|
||||
return (
|
||||
lambda x: np.exp(cs(np.log(x.to(u.MeV).value))) * u.MeV * u.cm**2 / u.g
|
||||
)
|
||||
|
||||
class C:
|
||||
_is_recognized_dtype: Callable[[DtypeObj], bool] = lambda x: lib.is_np_dtype(
|
||||
x, "M"
|
||||
) or isinstance(x, DatetimeTZDtype)
|
||||
|
||||
class C:
|
||||
def foo():
|
||||
if True:
|
||||
transaction_count = self._query_txs_for_range(
|
||||
get_count_fn=lambda from_ts, to_ts, _chain_id=chain_id: db_evmtx.count_transactions_in_range(
|
||||
chain_id=_chain_id,
|
||||
from_ts=from_ts,
|
||||
to_ts=to_ts,
|
||||
),
|
||||
)
|
||||
|
||||
transaction_count = self._query_txs_for_range(
|
||||
get_count_fn=lambda from_ts, to_ts, _chain_id=chain_id: db_evmtx.count_transactions_in_range[_chain_id, from_ts, to_ts],
|
||||
)
|
||||
|
||||
def ddb():
|
||||
sql = (
|
||||
lambda var, table, n=N: f"""
|
||||
CREATE TABLE {table} AS
|
||||
SELECT ROW_NUMBER() OVER () AS id, {var}
|
||||
FROM (
|
||||
SELECT {var}
|
||||
FROM RANGE({n}) _ ({var})
|
||||
ORDER BY RANDOM()
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
long_assignment_target.with_attribute.and_a_slice[with_an_index] = ( # 1
|
||||
# 2
|
||||
lambda x, y, z: # 3
|
||||
# 4
|
||||
x + y + z # 5
|
||||
# 6
|
||||
)
|
||||
|
||||
long_assignment_target.with_attribute.and_a_slice[with_an_index] = (
|
||||
lambda x, y, z: x + y + z
|
||||
)
|
||||
|
||||
long_assignment_target.with_attribute.and_a_slice[with_an_index] = lambda x, y, z: x + y + z
|
||||
|
||||
very_long_variable_name_x, very_long_variable_name_y = lambda a: a + some_very_long_expression, lambda b: b * another_very_long_expression_here
|
||||
|
||||
very_long_variable_name_for_result += lambda x: very_long_function_call_that_should_definitely_be_parenthesized_now(x, more_args, additional_parameters)
|
||||
|
||||
|
||||
if 1:
|
||||
if 2:
|
||||
if 3:
|
||||
if self.location in EVM_EVMLIKE_LOCATIONS and database is not None:
|
||||
exported_dict["notes"] = EVM_ADDRESS_REGEX.sub(
|
||||
repl=lambda matched_address: self._maybe_add_label_with_address(
|
||||
database=database,
|
||||
matched_address=matched_address,
|
||||
),
|
||||
string=exported_dict["notes"],
|
||||
)
|
||||
|
||||
class C:
|
||||
def f():
|
||||
return dict(
|
||||
filter(
|
||||
lambda intent_response: self.is_retrieval_intent_response(
|
||||
intent_response
|
||||
),
|
||||
self.responses.items(),
|
||||
)
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"op",
|
||||
[
|
||||
# Not fluent
|
||||
param(
|
||||
lambda left, right: (
|
||||
ibis.timestamp("2017-04-01")
|
||||
),
|
||||
),
|
||||
# These four are fluent and fit on one line inside the parenthesized
|
||||
# lambda body
|
||||
param(
|
||||
lambda left, right: (
|
||||
ibis.timestamp("2017-04-01").cast(dt.date)
|
||||
),
|
||||
),
|
||||
param(
|
||||
lambda left, right: (
|
||||
ibis.timestamp("2017-04-01").cast(dt.date).between(left, right)
|
||||
),
|
||||
),
|
||||
param(lambda left, right: ibis.timestamp("2017-04-01").cast(dt.date)),
|
||||
param(lambda left, right: ibis.timestamp("2017-04-01").cast(dt.date).between(left, right)),
|
||||
# This is too long on one line in the lambda body and gets wrapped
|
||||
# inside the body.
|
||||
param(
|
||||
lambda left, right: (
|
||||
ibis.timestamp("2017-04-01").cast(dt.date).between(left, right).between(left, right)
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_string_temporal_compare_between(con, op, left, right): ...
|
||||
|
||||
[
|
||||
(
|
||||
lambda eval_df, _: MetricValue(
|
||||
scores=eval_df["prediction"].tolist(),
|
||||
aggregate_results={"prediction_sum": sum(eval_df["prediction"])},
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
# reuses the list parentheses
|
||||
lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: [xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz]
|
||||
|
||||
# adds parentheses around the body
|
||||
lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: xxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyy + zzzzzzzzzzzzzzzzzzzz
|
||||
|
||||
# removes parentheses around the body
|
||||
lambda xxxxxxxxxxxxxxxxxxxx: (xxxxxxxxxxxxxxxxxxxx + 1)
|
||||
|
||||
mapper = lambda x: dict_with_default[np.nan if isinstance(x, float) and np.isnan(x) else x]
|
||||
|
||||
lambda x, y, z: (
|
||||
x + y + z
|
||||
)
|
||||
|
||||
lambda x, y, z: (
|
||||
x + y + z
|
||||
# trailing body
|
||||
)
|
||||
|
||||
lambda x, y, z: (
|
||||
x + y + z # trailing eol body
|
||||
)
|
||||
|
||||
lambda x, y, z: (
|
||||
x + y + z
|
||||
) # trailing lambda
|
||||
|
||||
lambda x, y, z: (
|
||||
# leading body
|
||||
x + y + z
|
||||
)
|
||||
|
||||
lambda x, y, z: ( # leading eol body
|
||||
x + y + z
|
||||
)
|
||||
|
||||
(
|
||||
lambda name:
|
||||
source_bucket # trailing eol comment
|
||||
if name == source_bucket_name
|
||||
else storage.Bucket(mock_service, destination_bucket_name)
|
||||
)
|
||||
|
||||
(
|
||||
lambda name:
|
||||
# dangling header comment
|
||||
source_bucket
|
||||
if name == source_bucket_name
|
||||
else storage.Bucket(mock_service, destination_bucket_name)
|
||||
)
|
||||
|
||||
x = (
|
||||
lambda name:
|
||||
# dangling header comment
|
||||
source_bucket
|
||||
if name == source_bucket_name
|
||||
else storage.Bucket(mock_service, destination_bucket_name)
|
||||
)
|
||||
|
||||
(
|
||||
lambda name: # dangling header comment
|
||||
(
|
||||
source_bucket
|
||||
if name == source_bucket_name
|
||||
else storage.Bucket(mock_service, destination_bucket_name)
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda from_ts, to_ts, _chain_id=chain_id: # dangling eol header comment
|
||||
db_evmtx.count_transactions_in_range(
|
||||
chain_id=_chain_id,
|
||||
from_ts=from_ts,
|
||||
to_ts=to_ts,
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda from_ts, to_ts, _chain_id=chain_id:
|
||||
# dangling header comment before call
|
||||
db_evmtx.count_transactions_in_range(
|
||||
chain_id=_chain_id,
|
||||
from_ts=from_ts,
|
||||
to_ts=to_ts,
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda left, right:
|
||||
# comment
|
||||
ibis.timestamp("2017-04-01").cast(dt.date).between(left, right)
|
||||
)
|
||||
|
||||
(
|
||||
lambda left, right:
|
||||
ibis.timestamp("2017-04-01") # comment
|
||||
.cast(dt.date)
|
||||
.between(left, right)
|
||||
)
|
||||
|
||||
(
|
||||
lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy:
|
||||
# comment
|
||||
[xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz]
|
||||
)
|
||||
|
||||
(
|
||||
lambda x, y:
|
||||
# comment
|
||||
{
|
||||
"key": x,
|
||||
"another": y,
|
||||
}
|
||||
)
|
||||
|
||||
(
|
||||
lambda x, y:
|
||||
# comment
|
||||
(
|
||||
x,
|
||||
y,
|
||||
z
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda x:
|
||||
# comment
|
||||
dict_with_default[np.nan if isinstance(x, float) and np.isnan(x) else x]
|
||||
)
|
||||
|
||||
(
|
||||
lambda from_ts, to_ts, _chain_id=chain_id:
|
||||
db_evmtx.count_transactions_in_range[
|
||||
# comment
|
||||
_chain_id, from_ts, to_ts
|
||||
]
|
||||
)
|
||||
|
||||
(
|
||||
lambda
|
||||
# comment
|
||||
*args, **kwargs:
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs) + 1
|
||||
)
|
||||
|
||||
(
|
||||
lambda # comment
|
||||
*args, **kwargs:
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs) + 1
|
||||
)
|
||||
|
||||
(
|
||||
lambda # comment 1
|
||||
# comment 2
|
||||
*args, **kwargs: # comment 3
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs) + 1
|
||||
)
|
||||
|
||||
(
|
||||
lambda # comment 1
|
||||
*args, **kwargs: # comment 3
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs) + 1
|
||||
)
|
||||
|
||||
(
|
||||
lambda *args, **kwargs:
|
||||
# comment 1
|
||||
( # comment 2
|
||||
# comment 3
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs) + 1 # comment 4
|
||||
# comment 5
|
||||
) # comment 6
|
||||
)
|
||||
|
||||
(
|
||||
lambda *brgs, **kwargs:
|
||||
# comment 1
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( # comment 2
|
||||
# comment 3
|
||||
*brgs, **kwargs) + 1 # comment 4
|
||||
# comment 5
|
||||
)
|
||||
|
||||
(
|
||||
lambda *crgs, **kwargs: # comment 1
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*crgs, **kwargs) + 1
|
||||
)
|
||||
|
||||
(
|
||||
lambda *drgs, **kwargs: # comment 1
|
||||
(
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*drgs, **kwargs) + 1
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda * # comment 1
|
||||
ergs, **
|
||||
# comment 2
|
||||
kwargs # comment 3
|
||||
: # comment 4
|
||||
(
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*ergs, **kwargs) + 1
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda # 1
|
||||
# 2
|
||||
left, # 3
|
||||
# 4
|
||||
right: # 5
|
||||
# 6
|
||||
ibis.timestamp("2017-04-01").cast(dt.date).between(left, right)
|
||||
)
|
||||
|
||||
(
|
||||
lambda x: # outer comment 1
|
||||
(
|
||||
lambda y: # inner comment 1
|
||||
# inner comment 2
|
||||
lambda z: (
|
||||
# innermost comment
|
||||
x + y + z
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
foo(
|
||||
lambda from_ts, # comment prevents collapsing the parameters to one line
|
||||
to_ts, _chain_id=chain_id: db_evmtx.count_transactions_in_range(
|
||||
chain_id=_chain_id,
|
||||
from_ts=from_ts,
|
||||
to_ts=to_ts,
|
||||
)
|
||||
)
|
||||
|
||||
foo(
|
||||
lambda from_ts, # but still wrap the body if it gets too long
|
||||
to_ts,
|
||||
_chain_id=chain_id: db_evmtx.count_transactions_in_rangeeeeeeeeeeeeeeeeeeeeeeeeeeeee(
|
||||
chain_id=_chain_id,
|
||||
from_ts=from_ts,
|
||||
to_ts=to_ts,
|
||||
)
|
||||
)
|
||||
|
||||
transform = lambda left, right: ibis.timestamp("2017-04-01").cast(dt.date).between(left, right).between(left, right) # trailing comment
|
||||
|
||||
(
|
||||
lambda: # comment
|
||||
1
|
||||
)
|
||||
|
||||
(
|
||||
lambda # comment
|
||||
:
|
||||
1
|
||||
)
|
||||
|
||||
(
|
||||
lambda:
|
||||
# comment
|
||||
1
|
||||
)
|
||||
|
||||
(
|
||||
lambda: # comment 1
|
||||
# comment 2
|
||||
1
|
||||
)
|
||||
|
||||
(
|
||||
lambda # comment 1
|
||||
# comment 2
|
||||
: # comment 3
|
||||
# comment 4
|
||||
1
|
||||
)
|
||||
|
||||
(
|
||||
lambda
|
||||
* # comment 2
|
||||
|
|
@ -271,3 +789,18 @@ def a():
|
|||
x:
|
||||
x
|
||||
)
|
||||
|
||||
(
|
||||
lambda: # dangling-end-of-line
|
||||
# dangling-own-line
|
||||
( # leading-body-end-of-line
|
||||
x
|
||||
)
|
||||
)
|
||||
|
||||
(
|
||||
lambda: # dangling-end-of-line
|
||||
( # leading-body-end-of-line
|
||||
x
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use ruff_formatter::{Argument, Arguments, write};
|
||||
use ruff_formatter::{Argument, Arguments, format_args, write};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::context::{NodeLevel, WithNodeLevel};
|
||||
|
|
@ -33,20 +33,27 @@ impl<'ast> Format<PyFormatContext<'ast>> for ParenthesizeIfExpands<'_, 'ast> {
|
|||
{
|
||||
let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);
|
||||
|
||||
write!(
|
||||
f,
|
||||
[group(&format_with(|f| {
|
||||
if_group_breaks(&token("(")).fmt(f)?;
|
||||
|
||||
if self.indent {
|
||||
soft_block_indent(&Arguments::from(&self.inner)).fmt(f)?;
|
||||
} else {
|
||||
Arguments::from(&self.inner).fmt(f)?;
|
||||
}
|
||||
|
||||
if_group_breaks(&token(")")).fmt(f)
|
||||
}))]
|
||||
)
|
||||
if self.indent {
|
||||
let parens_id = f.group_id("indented_parenthesize_if_expands");
|
||||
group(&format_args![
|
||||
if_group_breaks(&token("(")),
|
||||
indent_if_group_breaks(
|
||||
&format_args![soft_line_break(), &Arguments::from(&self.inner)],
|
||||
parens_id
|
||||
),
|
||||
soft_line_break(),
|
||||
if_group_breaks(&token(")"))
|
||||
])
|
||||
.with_id(Some(parens_id))
|
||||
.fmt(&mut f)
|
||||
} else {
|
||||
group(&format_args![
|
||||
if_group_breaks(&token("(")),
|
||||
Arguments::from(&self.inner),
|
||||
if_group_breaks(&token(")")),
|
||||
])
|
||||
.fmt(&mut f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,21 @@
|
|||
use ruff_formatter::write;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::ExprLambda;
|
||||
use ruff_formatter::{FormatRuleWithOptions, RemoveSoftLinesBuffer, format_args, write};
|
||||
use ruff_python_ast::{AnyNodeRef, Expr, ExprLambda};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::comments::dangling_comments;
|
||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
|
||||
use crate::builders::parenthesize_if_expands;
|
||||
use crate::comments::{SourceComment, dangling_comments, leading_comments, trailing_comments};
|
||||
use crate::expression::parentheses::{
|
||||
NeedsParentheses, OptionalParentheses, Parentheses, is_expression_parenthesized,
|
||||
};
|
||||
use crate::expression::{CallChainLayout, has_own_parentheses};
|
||||
use crate::other::parameters::ParametersParentheses;
|
||||
use crate::prelude::*;
|
||||
use crate::preview::is_parenthesize_lambda_bodies_enabled;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatExprLambda;
|
||||
pub struct FormatExprLambda {
|
||||
layout: ExprLambdaLayout,
|
||||
}
|
||||
|
||||
impl FormatNodeRule<ExprLambda> for FormatExprLambda {
|
||||
fn fmt_fields(&self, item: &ExprLambda, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
|
|
@ -20,13 +26,19 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
|
|||
body,
|
||||
} = item;
|
||||
|
||||
let body = &**body;
|
||||
let parameters = parameters.as_deref();
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling = comments.dangling(item);
|
||||
let preview = is_parenthesize_lambda_bodies_enabled(f.context());
|
||||
|
||||
write!(f, [token("lambda")])?;
|
||||
|
||||
if let Some(parameters) = parameters {
|
||||
// In this context, a dangling comment can either be a comment between the `lambda` the
|
||||
// Format any dangling comments before the parameters, but save any dangling comments after
|
||||
// the parameters/after the header to be formatted with the body below.
|
||||
let dangling_header_comments = if let Some(parameters) = parameters {
|
||||
// In this context, a dangling comment can either be a comment between the `lambda` and the
|
||||
// parameters, or a comment between the parameters and the body.
|
||||
let (dangling_before_parameters, dangling_after_parameters) = dangling
|
||||
.split_at(dangling.partition_point(|comment| comment.end() < parameters.start()));
|
||||
|
|
@ -86,7 +98,7 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
|
|||
// *x: x
|
||||
// )
|
||||
// ```
|
||||
if comments.has_leading(&**parameters) {
|
||||
if comments.has_leading(parameters) {
|
||||
hard_line_break().fmt(f)?;
|
||||
} else {
|
||||
write!(f, [space()])?;
|
||||
|
|
@ -95,32 +107,90 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
|
|||
write!(f, [dangling_comments(dangling_before_parameters)])?;
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
[parameters
|
||||
.format()
|
||||
.with_options(ParametersParentheses::Never)]
|
||||
)?;
|
||||
|
||||
write!(f, [token(":")])?;
|
||||
|
||||
if dangling_after_parameters.is_empty() {
|
||||
write!(f, [space()])?;
|
||||
// Try to keep the parameters on a single line, unless there are intervening comments.
|
||||
if preview && !comments.contains_comments(parameters.into()) {
|
||||
let mut buffer = RemoveSoftLinesBuffer::new(f);
|
||||
write!(
|
||||
buffer,
|
||||
[parameters
|
||||
.format()
|
||||
.with_options(ParametersParentheses::Never)]
|
||||
)?;
|
||||
} else {
|
||||
write!(f, [dangling_comments(dangling_after_parameters)])?;
|
||||
write!(
|
||||
f,
|
||||
[parameters
|
||||
.format()
|
||||
.with_options(ParametersParentheses::Never)]
|
||||
)?;
|
||||
}
|
||||
|
||||
dangling_after_parameters
|
||||
} else {
|
||||
write!(f, [token(":")])?;
|
||||
dangling
|
||||
};
|
||||
|
||||
// In this context, a dangling comment is a comment between the `lambda` and the body.
|
||||
if dangling.is_empty() {
|
||||
write!(f, [space()])?;
|
||||
} else {
|
||||
write!(f, [dangling_comments(dangling)])?;
|
||||
}
|
||||
write!(f, [token(":")])?;
|
||||
|
||||
if dangling_header_comments.is_empty() {
|
||||
write!(f, [space()])?;
|
||||
} else if !preview {
|
||||
write!(f, [dangling_comments(dangling_header_comments)])?;
|
||||
}
|
||||
|
||||
write!(f, [body.format()])
|
||||
if !preview {
|
||||
return body.format().fmt(f);
|
||||
}
|
||||
|
||||
let fmt_body = FormatBody {
|
||||
body,
|
||||
dangling_header_comments,
|
||||
};
|
||||
|
||||
match self.layout {
|
||||
ExprLambdaLayout::Assignment => fits_expanded(&fmt_body).fmt(f),
|
||||
ExprLambdaLayout::Default => fmt_body.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub enum ExprLambdaLayout {
|
||||
#[default]
|
||||
Default,
|
||||
|
||||
/// The [`ExprLambda`] is the direct child of an assignment expression, so it needs to use
|
||||
/// `fits_expanded` to prefer parenthesizing its own body before the assignment tries to
|
||||
/// parenthesize the whole lambda. For example, we want this formatting:
|
||||
///
|
||||
/// ```py
|
||||
/// long_assignment_target = lambda x, y, z: (
|
||||
/// x + y + z
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// instead of either of these:
|
||||
///
|
||||
/// ```py
|
||||
/// long_assignment_target = (
|
||||
/// lambda x, y, z: (
|
||||
/// x + y + z
|
||||
/// )
|
||||
/// )
|
||||
///
|
||||
/// long_assignment_target = (
|
||||
/// lambda x, y, z: x + y + z
|
||||
/// )
|
||||
/// ```
|
||||
Assignment,
|
||||
}
|
||||
|
||||
impl FormatRuleWithOptions<ExprLambda, PyFormatContext<'_>> for FormatExprLambda {
|
||||
type Options = ExprLambdaLayout;
|
||||
|
||||
fn with_options(mut self, options: Self::Options) -> Self {
|
||||
self.layout = options;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,3 +207,266 @@ impl NeedsParentheses for ExprLambda {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FormatBody<'a> {
|
||||
body: &'a Expr,
|
||||
|
||||
/// Dangling comments attached to the lambda header that should be formatted with the body.
|
||||
///
|
||||
/// These can include both own-line and end-of-line comments. For lambdas with parameters, this
|
||||
/// means comments after the parameters:
|
||||
///
|
||||
/// ```py
|
||||
/// (
|
||||
/// lambda x, y # 1
|
||||
/// # 2
|
||||
/// : # 3
|
||||
/// # 4
|
||||
/// x + y
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// Or all dangling comments for lambdas without parameters:
|
||||
///
|
||||
/// ```py
|
||||
/// (
|
||||
/// lambda # 1
|
||||
/// # 2
|
||||
/// : # 3
|
||||
/// # 4
|
||||
/// 1
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// In most cases these should formatted within the parenthesized body, as in:
|
||||
///
|
||||
/// ```py
|
||||
/// (
|
||||
/// lambda: ( # 1
|
||||
/// # 2
|
||||
/// # 3
|
||||
/// # 4
|
||||
/// 1
|
||||
/// )
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// or without `# 2`:
|
||||
///
|
||||
/// ```py
|
||||
/// (
|
||||
/// lambda: ( # 1 # 3
|
||||
/// # 4
|
||||
/// 1
|
||||
/// )
|
||||
/// )
|
||||
/// ```
|
||||
dangling_header_comments: &'a [SourceComment],
|
||||
}
|
||||
|
||||
impl Format<PyFormatContext<'_>> for FormatBody<'_> {
|
||||
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
let FormatBody {
|
||||
dangling_header_comments,
|
||||
body,
|
||||
} = self;
|
||||
|
||||
let body = *body;
|
||||
let comments = f.context().comments().clone();
|
||||
let body_comments = comments.leading_dangling_trailing(body);
|
||||
|
||||
if !dangling_header_comments.is_empty() {
|
||||
// Split the dangling header comments into trailing comments formatted with the lambda
|
||||
// header (1) and leading comments formatted with the body (2, 3, 4).
|
||||
//
|
||||
// ```python
|
||||
// (
|
||||
// lambda # 1
|
||||
// # 2
|
||||
// : # 3
|
||||
// # 4
|
||||
// y
|
||||
// )
|
||||
// ```
|
||||
//
|
||||
// Note that these are split based on their line position rather than using
|
||||
// `partition_point` based on a range, for example.
|
||||
let (trailing_header_comments, leading_body_comments) = dangling_header_comments
|
||||
.split_at(
|
||||
dangling_header_comments
|
||||
.iter()
|
||||
.position(|comment| comment.line_position().is_own_line())
|
||||
.unwrap_or(dangling_header_comments.len()),
|
||||
);
|
||||
|
||||
// If the body is parenthesized and has its own leading comments, preserve the
|
||||
// separation between the dangling lambda comments and the body comments. For
|
||||
// example, preserve this comment positioning:
|
||||
//
|
||||
// ```python
|
||||
// (
|
||||
// lambda: # 1
|
||||
// # 2
|
||||
// ( # 3
|
||||
// x
|
||||
// )
|
||||
// )
|
||||
// ```
|
||||
//
|
||||
// 1 and 2 are dangling on the lambda and emitted first, followed by a hard line
|
||||
// break and the parenthesized body with its leading comments.
|
||||
//
|
||||
// However, when removing 2, 1 and 3 can instead be formatted on the same line:
|
||||
//
|
||||
// ```python
|
||||
// (
|
||||
// lambda: ( # 1 # 3
|
||||
// x
|
||||
// )
|
||||
// )
|
||||
// ```
|
||||
let comments = f.context().comments();
|
||||
if is_expression_parenthesized(body.into(), comments.ranges(), f.context().source())
|
||||
&& comments.has_leading(body)
|
||||
{
|
||||
trailing_comments(dangling_header_comments).fmt(f)?;
|
||||
|
||||
// Note that `leading_body_comments` have already been formatted as part of
|
||||
// `dangling_header_comments` above, but their presence still determines the spacing
|
||||
// here.
|
||||
if leading_body_comments.is_empty() {
|
||||
space().fmt(f)?;
|
||||
} else {
|
||||
hard_line_break().fmt(f)?;
|
||||
}
|
||||
|
||||
body.format().with_options(Parentheses::Always).fmt(f)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
space(),
|
||||
token("("),
|
||||
trailing_comments(trailing_header_comments),
|
||||
block_indent(&format_args!(
|
||||
leading_comments(leading_body_comments),
|
||||
body.format().with_options(Parentheses::Never)
|
||||
)),
|
||||
token(")")
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
// If the body 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.
|
||||
else if body_comments.has_leading() || body_comments.has_trailing_own_line() {
|
||||
body.format().with_options(Parentheses::Always).fmt(f)
|
||||
}
|
||||
// Calls and subscripts require special formatting because they have their own
|
||||
// parentheses, but they can also have an arbitrary amount of text before the
|
||||
// opening parenthesis. We want to avoid cases where we keep a long callable on the
|
||||
// same line as the lambda parameters. For example, `db_evmtx...` in:
|
||||
//
|
||||
// ```py
|
||||
// transaction_count = self._query_txs_for_range(
|
||||
// get_count_fn=lambda from_ts, to_ts, _chain_id=chain_id: db_evmtx.count_transactions_in_range(
|
||||
// chain_id=_chain_id,
|
||||
// from_ts=from_ts,
|
||||
// to_ts=to_ts,
|
||||
// ),
|
||||
// )
|
||||
// ```
|
||||
//
|
||||
// should cause the whole lambda body to be parenthesized instead:
|
||||
//
|
||||
// ```py
|
||||
// transaction_count = self._query_txs_for_range(
|
||||
// get_count_fn=lambda from_ts, to_ts, _chain_id=chain_id: (
|
||||
// db_evmtx.count_transactions_in_range(
|
||||
// chain_id=_chain_id,
|
||||
// from_ts=from_ts,
|
||||
// to_ts=to_ts,
|
||||
// )
|
||||
// ),
|
||||
// )
|
||||
// ```
|
||||
else if matches!(body, Expr::Call(_) | Expr::Subscript(_)) {
|
||||
let unparenthesized = body.format().with_options(Parentheses::Never);
|
||||
if CallChainLayout::from_expression(
|
||||
body.into(),
|
||||
comments.ranges(),
|
||||
f.context().source(),
|
||||
) == CallChainLayout::Fluent
|
||||
{
|
||||
parenthesize_if_expands(&unparenthesized).fmt(f)
|
||||
} else {
|
||||
let unparenthesized = unparenthesized.memoized();
|
||||
if unparenthesized.inspect(f)?.will_break() {
|
||||
expand_parent().fmt(f)?;
|
||||
}
|
||||
|
||||
best_fitting![
|
||||
// body all flat
|
||||
unparenthesized,
|
||||
// body expanded
|
||||
group(&unparenthesized).should_expand(true),
|
||||
// parenthesized
|
||||
format_args![token("("), block_indent(&unparenthesized), token(")")]
|
||||
]
|
||||
.fmt(f)
|
||||
}
|
||||
}
|
||||
// For other cases with their own parentheses, such as lists, sets, dicts, tuples,
|
||||
// etc., we can just format the body directly. Their own formatting results in the
|
||||
// lambda being formatted well too. For example:
|
||||
//
|
||||
// ```py
|
||||
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: [xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz]
|
||||
// ```
|
||||
//
|
||||
// gets formatted as:
|
||||
//
|
||||
// ```py
|
||||
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: [
|
||||
// xxxxxxxxxxxxxxxxxxxx,
|
||||
// yyyyyyyyyyyyyyyyyyyy,
|
||||
// zzzzzzzzzzzzzzzzzzzz
|
||||
// ]
|
||||
// ```
|
||||
else if has_own_parentheses(body, f.context()).is_some() {
|
||||
body.format().fmt(f)
|
||||
}
|
||||
// Finally, for expressions without their own parentheses, use
|
||||
// `parenthesize_if_expands` to add parentheses around the body, only if it expands
|
||||
// across multiple lines. The `Parentheses::Never` here also removes unnecessary
|
||||
// parentheses around lambda bodies that fit on one line. For example:
|
||||
//
|
||||
// ```py
|
||||
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: xxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyy + zzzzzzzzzzzzzzzzzzzz
|
||||
// ```
|
||||
//
|
||||
// is formatted as:
|
||||
//
|
||||
// ```py
|
||||
// lambda xxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzzzz: (
|
||||
// xxxxxxxxxxxxxxxxxxxx + yyyyyyyyyyyyyyyyyyyy + zzzzzzzzzzzzzzzzzzzz
|
||||
// )
|
||||
// ```
|
||||
//
|
||||
// while
|
||||
//
|
||||
// ```py
|
||||
// lambda xxxxxxxxxxxxxxxxxxxx: (xxxxxxxxxxxxxxxxxxxx + 1)
|
||||
// ```
|
||||
//
|
||||
// is formatted as:
|
||||
//
|
||||
// ```py
|
||||
// lambda xxxxxxxxxxxxxxxxxxxx: xxxxxxxxxxxxxxxxxxxx + 1
|
||||
// ```
|
||||
else {
|
||||
parenthesize_if_expands(&body.format().with_options(Parentheses::Never)).fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,3 +52,10 @@ pub(crate) const fn is_avoid_parens_for_long_as_captures_enabled(
|
|||
) -> bool {
|
||||
context.is_preview()
|
||||
}
|
||||
|
||||
/// Returns `true` if the
|
||||
/// [`parenthesize_lambda_bodies`](https://github.com/astral-sh/ruff/pull/21385) preview style is
|
||||
/// enabled.
|
||||
pub(crate) const fn is_parenthesize_lambda_bodies_enabled(context: &PyFormatContext) -> bool {
|
||||
context.is_preview()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -906,11 +906,10 @@ x = {
|
|||
-)
|
||||
+string_with_escaped_nameescape = "........................................................................... \\N{LAO KO LA}"
|
||||
|
||||
-msg = lambda x: (
|
||||
msg = lambda x: (
|
||||
- f"this is a very very very very long lambda value {x} that doesn't fit on a"
|
||||
- " single line"
|
||||
+msg = (
|
||||
+ lambda x: f"this is a very very very very long lambda value {x} that doesn't fit on a single line"
|
||||
+ f"this is a very very very very long lambda value {x} that doesn't fit on a single line"
|
||||
)
|
||||
|
||||
dict_with_lambda_values = {
|
||||
|
|
@ -1403,8 +1402,8 @@ string_with_escaped_nameescape = "..............................................
|
|||
|
||||
string_with_escaped_nameescape = "........................................................................... \\N{LAO KO LA}"
|
||||
|
||||
msg = (
|
||||
lambda x: f"this is a very very very very long lambda value {x} that doesn't fit on a single line"
|
||||
msg = lambda x: (
|
||||
f"this is a very very very very long lambda value {x} that doesn't fit on a single line"
|
||||
)
|
||||
|
||||
dict_with_lambda_values = {
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ a = b if """
|
|||
# Another use case
|
||||
data = yaml.load("""\
|
||||
a: 1
|
||||
@@ -77,19 +106,23 @@
|
||||
@@ -77,10 +106,12 @@
|
||||
b: 2
|
||||
""",
|
||||
)
|
||||
|
|
@ -390,19 +390,7 @@ a = b if """
|
|||
|
||||
MULTILINE = """
|
||||
foo
|
||||
""".replace("\n", "")
|
||||
-generated_readme = lambda project_name: """
|
||||
+generated_readme = (
|
||||
+ lambda project_name: """
|
||||
{}
|
||||
|
||||
<Add content here!>
|
||||
""".strip().format(project_name)
|
||||
+)
|
||||
parser.usage += """
|
||||
Custom extra help summary.
|
||||
|
||||
@@ -156,16 +189,24 @@
|
||||
@@ -156,16 +187,24 @@
|
||||
10 LOAD_CONST 0 (None)
|
||||
12 RETURN_VALUE
|
||||
""" % (_C.__init__.__code__.co_firstlineno + 1,)
|
||||
|
|
@ -433,7 +421,7 @@ a = b if """
|
|||
[
|
||||
"""cow
|
||||
moos""",
|
||||
@@ -206,7 +247,9 @@
|
||||
@@ -206,7 +245,9 @@
|
||||
"c"
|
||||
)
|
||||
|
||||
|
|
@ -444,7 +432,7 @@ a = b if """
|
|||
|
||||
assert some_var == expected_result, """
|
||||
test
|
||||
@@ -224,10 +267,8 @@
|
||||
@@ -224,10 +265,8 @@
|
||||
"""Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx
|
||||
xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx"""
|
||||
),
|
||||
|
|
@ -457,7 +445,7 @@ a = b if """
|
|||
},
|
||||
}
|
||||
|
||||
@@ -246,14 +287,12 @@
|
||||
@@ -246,14 +285,12 @@
|
||||
a
|
||||
a"""
|
||||
),
|
||||
|
|
@ -597,13 +585,11 @@ data = yaml.load(
|
|||
MULTILINE = """
|
||||
foo
|
||||
""".replace("\n", "")
|
||||
generated_readme = (
|
||||
lambda project_name: """
|
||||
generated_readme = lambda project_name: """
|
||||
{}
|
||||
|
||||
<Add content here!>
|
||||
""".strip().format(project_name)
|
||||
)
|
||||
parser.usage += """
|
||||
Custom extra help summary.
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/multiline_string_deviations.py
|
||||
snapshot_kind: text
|
||||
---
|
||||
## Input
|
||||
```python
|
||||
|
|
@ -106,3 +105,22 @@ generated_readme = (
|
|||
""".strip().format(project_name)
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -44,10 +44,8 @@
|
||||
# this by changing `Lambda::needs_parentheses` to return `BestFit` but it causes
|
||||
# issues when the lambda has comments.
|
||||
# Let's keep this as a known deviation for now.
|
||||
-generated_readme = (
|
||||
- lambda project_name: """
|
||||
+generated_readme = lambda project_name: """
|
||||
{}
|
||||
|
||||
<Add content here!>
|
||||
""".strip().format(project_name)
|
||||
-)
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in New Issue