Parenthesize long type annotations in annotated assignments (#9210)

This commit is contained in:
Micha Reiser 2023-12-22 12:33:47 +09:00 committed by GitHub
parent 3cc719bd74
commit fa2c37b411
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 777 additions and 47 deletions

View file

@ -2,9 +2,13 @@ use ruff_formatter::write;
use ruff_python_ast::StmtAnnAssign;
use crate::comments::{SourceComment, SuppressionKind};
use crate::expression::has_parentheses;
use crate::expression::parentheses::Parentheses;
use crate::expression::{has_parentheses, is_splittable_expression};
use crate::prelude::*;
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
use crate::preview::{
is_parenthesize_long_type_hints_enabled,
is_prefer_splitting_right_hand_side_of_assignments_enabled,
};
use crate::statement::stmt_assign::{
AnyAssignmentOperator, AnyBeforeOperator, FormatStatementsLastExpression,
};
@ -27,7 +31,11 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
if let Some(value) = value {
if is_prefer_splitting_right_hand_side_of_assignments_enabled(f.context())
&& has_parentheses(annotation, f.context()).is_some()
// The `has_parentheses` check can be removed when stabilizing `is_parenthesize_long_type_hints`.
// because `is_splittable_expression` covers both.
&& (has_parentheses(annotation, f.context()).is_some()
|| (is_parenthesize_long_type_hints_enabled(f.context())
&& is_splittable_expression(annotation, f.context())))
{
FormatStatementsLastExpression::RightToLeft {
before_operator: AnyBeforeOperator::Expression(annotation),
@ -37,10 +45,28 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
}
.fmt(f)?;
} else {
// Remove unnecessary parentheses around the annotation if the parenthesize long type hints preview style is enabled.
// Ensure we keep the parentheses if the annotation has any comments.
if is_parenthesize_long_type_hints_enabled(f.context()) {
if f.context().comments().has_leading(annotation.as_ref())
|| f.context().comments().has_trailing(annotation.as_ref())
{
annotation
.format()
.with_options(Parentheses::Always)
.fmt(f)?;
} else {
annotation
.format()
.with_options(Parentheses::Never)
.fmt(f)?;
}
} else {
annotation.format().fmt(f)?;
}
write!(
f,
[
annotation.format(),
space(),
token("="),
space(),
@ -49,7 +75,19 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
)?;
}
} else {
annotation.format().fmt(f)?;
// Parenthesize the value and inline the comment if it is a "simple" type annotation, similar
// to what we do with the value.
// ```python
// class Test:
// safe_age: (
// Decimal # the user's age, used to determine if it's safe for them to use ruff
// )
// ```
if is_parenthesize_long_type_hints_enabled(f.context()) {
FormatStatementsLastExpression::left_to_right(annotation, item).fmt(f)?;
} else {
annotation.format().fmt(f)?;
}
}
if f.options().source_type().is_ipynb()

View file

@ -9,11 +9,18 @@ use crate::comments::{
};
use crate::context::{NodeLevel, WithNodeLevel};
use crate::expression::parentheses::{
is_expression_parenthesized, NeedsParentheses, OptionalParentheses, Parentheses, Parenthesize,
is_expression_parenthesized, optional_parentheses, NeedsParentheses, OptionalParentheses,
Parentheses, Parenthesize,
};
use crate::expression::{
can_omit_optional_parentheses, has_own_parentheses, has_parentheses,
maybe_parenthesize_expression,
};
use crate::expression::{has_own_parentheses, has_parentheses, maybe_parenthesize_expression};
use crate::prelude::*;
use crate::preview::is_prefer_splitting_right_hand_side_of_assignments_enabled;
use crate::preview::{
is_parenthesize_long_type_hints_enabled,
is_prefer_splitting_right_hand_side_of_assignments_enabled,
};
use crate::statement::trailing_semicolon;
#[derive(Default)]
@ -686,8 +693,17 @@ impl Format<PyFormatContext<'_>> for AnyBeforeOperator<'_> {
}
// Never parenthesize targets that come with their own parentheses, e.g. don't parenthesize lists or dictionary literals.
else if should_parenthesize_target(expression, f.context()) {
parenthesize_if_expands(&expression.format().with_options(Parentheses::Never))
if is_parenthesize_long_type_hints_enabled(f.context())
&& can_omit_optional_parentheses(expression, f.context())
{
optional_parentheses(&expression.format().with_options(Parentheses::Never))
.fmt(f)
} else {
parenthesize_if_expands(
&expression.format().with_options(Parentheses::Never),
)
.fmt(f)
}
} else {
expression.format().with_options(Parentheses::Never).fmt(f)
}