mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:10 +00:00
Parenthesize long type annotations in annotated assignments (#9210)
This commit is contained in:
parent
3cc719bd74
commit
fa2c37b411
8 changed files with 777 additions and 47 deletions
157
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/long_type_annotations.py
vendored
Normal file
157
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/long_type_annotations.py
vendored
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
x1: A[b] | EventHandler | EventSpec | list[EventHandler | EventSpec] | Other | More | AndMore | None = None
|
||||||
|
|
||||||
|
x2: "VeryLongClassNameWithAwkwardGenericSubtype[int] |" "VeryLongClassNameWithAwkwardGenericSubtype[str]"
|
||||||
|
|
||||||
|
x6: VeryLongClassNameWithAwkwardGenericSubtype[
|
||||||
|
integeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer,
|
||||||
|
VeryLongClassNameWithAwkwardGenericSubtype,
|
||||||
|
str
|
||||||
|
] = True
|
||||||
|
|
||||||
|
|
||||||
|
x7: CustomTrainingJob | CustomContainerTrainingJob | CustomPythonPackageTrainingJob
|
||||||
|
x8: (
|
||||||
|
None
|
||||||
|
| datasets.ImageDataset
|
||||||
|
| datasets.TabularDataset
|
||||||
|
| datasets.TextDataset
|
||||||
|
| datasets.VideoDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
x9: None | (
|
||||||
|
datasets.ImageDataset
|
||||||
|
| datasets.TabularDataset
|
||||||
|
| datasets.TextDataset
|
||||||
|
| datasets.VideoDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
|
||||||
|
x10: (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaa[
|
||||||
|
bbbbbbbbbbb,
|
||||||
|
Subscript
|
||||||
|
| None
|
||||||
|
| datasets.ImageDataset
|
||||||
|
| datasets.TabularDataset
|
||||||
|
| datasets.TextDataset
|
||||||
|
| datasets.VideoDataset,
|
||||||
|
],
|
||||||
|
bbb[other],
|
||||||
|
) = None
|
||||||
|
|
||||||
|
x11: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] = None
|
||||||
|
|
||||||
|
x12: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | Other = None
|
||||||
|
|
||||||
|
|
||||||
|
x13: [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | Other = None
|
||||||
|
|
||||||
|
x14: [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] = None
|
||||||
|
|
||||||
|
x15: [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | Other = None
|
||||||
|
|
||||||
|
x16: None | Literal[
|
||||||
|
"split",
|
||||||
|
"a bit longer",
|
||||||
|
"records",
|
||||||
|
"index",
|
||||||
|
"table",
|
||||||
|
"columns",
|
||||||
|
"values",
|
||||||
|
] = None
|
||||||
|
|
||||||
|
x17: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Test:
|
||||||
|
safe_age: Decimal # the user's age, used to determine if it's safe for them to use ruff
|
||||||
|
applied_fixes: int # the number of fixes that this user applied. Used for ranking the users with the most applied fixes.
|
||||||
|
string_annotation: "Test" # a long comment after a quoted, runtime-only type annotation
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Comments
|
||||||
|
|
||||||
|
leading: (
|
||||||
|
# Leading comment
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
)
|
||||||
|
|
||||||
|
leading_with_value: (
|
||||||
|
# Leading comment
|
||||||
|
None
|
||||||
|
| dataset.ImageDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
leading_open_parentheses: ( # Leading comment
|
||||||
|
None
|
||||||
|
| dataset.ImageDataset
|
||||||
|
)
|
||||||
|
|
||||||
|
leading_open_parentheses_with_value: ( # Leading comment
|
||||||
|
None
|
||||||
|
| dataset.ImageDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
trailing: (
|
||||||
|
None | dataset.ImageDataset # trailing comment
|
||||||
|
)
|
||||||
|
|
||||||
|
trailing_with_value: (
|
||||||
|
None | dataset.ImageDataset # trailing comment
|
||||||
|
) = None
|
||||||
|
|
||||||
|
trailing_own_line: (
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
# trailing own line
|
||||||
|
)
|
||||||
|
|
||||||
|
trailing_own_line_with_value: (
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
# trailing own line
|
||||||
|
) = None
|
||||||
|
|
||||||
|
nested_comment: None | [
|
||||||
|
# a list of strings
|
||||||
|
str
|
||||||
|
] = None
|
|
@ -529,7 +529,7 @@ impl<'ast> IntoFormat<PyFormatContext<'ast>> for Expr {
|
||||||
///
|
///
|
||||||
/// This mimics Black's [`_maybe_split_omitting_optional_parens`](https://github.com/psf/black/blob/d1248ca9beaf0ba526d265f4108836d89cf551b7/src/black/linegen.py#L746-L820)
|
/// This mimics Black's [`_maybe_split_omitting_optional_parens`](https://github.com/psf/black/blob/d1248ca9beaf0ba526d265f4108836d89cf551b7/src/black/linegen.py#L746-L820)
|
||||||
#[allow(clippy::if_same_then_else)]
|
#[allow(clippy::if_same_then_else)]
|
||||||
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);
|
let mut visitor = CanOmitOptionalParenthesesVisitor::new(context);
|
||||||
visitor.visit_subexpression(expr);
|
visitor.visit_subexpression(expr);
|
||||||
|
|
||||||
|
@ -1195,3 +1195,75 @@ impl From<Operator> for OperatorPrecedence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `expr` is an expression that can be split into multiple lines.
|
||||||
|
///
|
||||||
|
/// Returns `false` for expressions that are guaranteed to never split.
|
||||||
|
pub(crate) fn is_splittable_expression(expr: &Expr, context: &PyFormatContext) -> bool {
|
||||||
|
match expr {
|
||||||
|
// Single token expressions. They never have any split points.
|
||||||
|
Expr::NamedExpr(_)
|
||||||
|
| Expr::Name(_)
|
||||||
|
| Expr::NumberLiteral(_)
|
||||||
|
| Expr::BooleanLiteral(_)
|
||||||
|
| Expr::NoneLiteral(_)
|
||||||
|
| Expr::EllipsisLiteral(_)
|
||||||
|
| Expr::Slice(_)
|
||||||
|
| Expr::IpyEscapeCommand(_) => false,
|
||||||
|
|
||||||
|
// Expressions that insert split points when parenthesized.
|
||||||
|
Expr::Compare(_)
|
||||||
|
| Expr::BinOp(_)
|
||||||
|
| Expr::BoolOp(_)
|
||||||
|
| Expr::IfExp(_)
|
||||||
|
| Expr::GeneratorExp(_)
|
||||||
|
| Expr::Subscript(_)
|
||||||
|
| Expr::Await(_)
|
||||||
|
| Expr::ListComp(_)
|
||||||
|
| Expr::SetComp(_)
|
||||||
|
| Expr::DictComp(_)
|
||||||
|
| Expr::YieldFrom(_) => true,
|
||||||
|
|
||||||
|
// Sequence types can split if they contain at least one element.
|
||||||
|
Expr::Tuple(tuple) => !tuple.elts.is_empty(),
|
||||||
|
Expr::Dict(dict) => !dict.values.is_empty(),
|
||||||
|
Expr::Set(set) => !set.elts.is_empty(),
|
||||||
|
Expr::List(list) => !list.elts.is_empty(),
|
||||||
|
|
||||||
|
Expr::UnaryOp(unary) => is_splittable_expression(unary.operand.as_ref(), context),
|
||||||
|
Expr::Yield(ast::ExprYield { value, .. }) => value.is_some(),
|
||||||
|
|
||||||
|
Expr::Call(ast::ExprCall {
|
||||||
|
arguments, func, ..
|
||||||
|
}) => {
|
||||||
|
!arguments.is_empty()
|
||||||
|
|| is_expression_parenthesized(
|
||||||
|
func.as_ref().into(),
|
||||||
|
context.comments().ranges(),
|
||||||
|
context.source(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String like literals can expand if they are implicit concatenated.
|
||||||
|
Expr::FString(fstring) => fstring.value.is_implicit_concatenated(),
|
||||||
|
Expr::StringLiteral(string) => string.value.is_implicit_concatenated(),
|
||||||
|
Expr::BytesLiteral(bytes) => bytes.value.is_implicit_concatenated(),
|
||||||
|
|
||||||
|
// Expressions that have no split points per se, but they contain nested sub expressions that might expand.
|
||||||
|
Expr::Lambda(ast::ExprLambda {
|
||||||
|
body: expression, ..
|
||||||
|
})
|
||||||
|
| Expr::Starred(ast::ExprStarred {
|
||||||
|
value: expression, ..
|
||||||
|
})
|
||||||
|
| Expr::Attribute(ast::ExprAttribute {
|
||||||
|
value: expression, ..
|
||||||
|
}) => {
|
||||||
|
is_expression_parenthesized(
|
||||||
|
expression.into(),
|
||||||
|
context.comments().ranges(),
|
||||||
|
context.source(),
|
||||||
|
) || is_splittable_expression(expression.as_ref(), context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,11 @@ pub(crate) const fn is_prefer_splitting_right_hand_side_of_assignments_enabled(
|
||||||
context.is_preview()
|
context.is_preview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the [`parenthesize_long_type_hints`](https://github.com/astral-sh/ruff/issues/8894) preview style is enabled.
|
||||||
|
pub(crate) const fn is_parenthesize_long_type_hints_enabled(context: &PyFormatContext) -> bool {
|
||||||
|
context.is_preview()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if the [`no_blank_line_before_class_docstring`] preview style is enabled.
|
/// Returns `true` if the [`no_blank_line_before_class_docstring`] preview style is enabled.
|
||||||
///
|
///
|
||||||
/// [`no_blank_line_before_class_docstring`]: https://github.com/astral-sh/ruff/issues/8888
|
/// [`no_blank_line_before_class_docstring`]: https://github.com/astral-sh/ruff/issues/8888
|
||||||
|
|
|
@ -2,9 +2,13 @@ use ruff_formatter::write;
|
||||||
use ruff_python_ast::StmtAnnAssign;
|
use ruff_python_ast::StmtAnnAssign;
|
||||||
|
|
||||||
use crate::comments::{SourceComment, SuppressionKind};
|
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::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::{
|
use crate::statement::stmt_assign::{
|
||||||
AnyAssignmentOperator, AnyBeforeOperator, FormatStatementsLastExpression,
|
AnyAssignmentOperator, AnyBeforeOperator, FormatStatementsLastExpression,
|
||||||
};
|
};
|
||||||
|
@ -27,7 +31,11 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
|
||||||
|
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
if is_prefer_splitting_right_hand_side_of_assignments_enabled(f.context())
|
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 {
|
FormatStatementsLastExpression::RightToLeft {
|
||||||
before_operator: AnyBeforeOperator::Expression(annotation),
|
before_operator: AnyBeforeOperator::Expression(annotation),
|
||||||
|
@ -37,10 +45,28 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
|
||||||
}
|
}
|
||||||
.fmt(f)?;
|
.fmt(f)?;
|
||||||
} else {
|
} 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!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
annotation.format(),
|
|
||||||
space(),
|
space(),
|
||||||
token("="),
|
token("="),
|
||||||
space(),
|
space(),
|
||||||
|
@ -48,9 +74,21 @@ impl FormatNodeRule<StmtAnnAssign> for FormatStmtAnnAssign {
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 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 {
|
} else {
|
||||||
annotation.format().fmt(f)?;
|
annotation.format().fmt(f)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if f.options().source_type().is_ipynb()
|
if f.options().source_type().is_ipynb()
|
||||||
&& f.context().node_level().is_last_top_level_statement()
|
&& f.context().node_level().is_last_top_level_statement()
|
||||||
|
|
|
@ -9,11 +9,18 @@ use crate::comments::{
|
||||||
};
|
};
|
||||||
use crate::context::{NodeLevel, WithNodeLevel};
|
use crate::context::{NodeLevel, WithNodeLevel};
|
||||||
use crate::expression::parentheses::{
|
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::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;
|
use crate::statement::trailing_semicolon;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[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.
|
// 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()) {
|
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)
|
.fmt(f)
|
||||||
|
} else {
|
||||||
|
parenthesize_if_expands(
|
||||||
|
&expression.format().with_options(Parentheses::Never),
|
||||||
|
)
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
expression.format().with_options(Parentheses::Never).fmt(f)
|
expression.format().with_options(Parentheses::Never).fmt(f)
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,36 +95,7 @@ def f(
|
||||||
```diff
|
```diff
|
||||||
--- Black
|
--- Black
|
||||||
+++ Ruff
|
+++ Ruff
|
||||||
@@ -7,23 +7,13 @@
|
@@ -63,7 +63,7 @@
|
||||||
)
|
|
||||||
|
|
||||||
# "AnnAssign"s now also work
|
|
||||||
-z: (
|
|
||||||
- Loooooooooooooooooooooooong
|
|
||||||
- | Loooooooooooooooooooooooong
|
|
||||||
- | Loooooooooooooooooooooooong
|
|
||||||
- | Loooooooooooooooooooooooong
|
|
||||||
-)
|
|
||||||
-z: Short | Short2 | Short3 | Short4
|
|
||||||
-z: int
|
|
||||||
-z: int
|
|
||||||
+z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong
|
|
||||||
+z: (Short | Short2 | Short3 | Short4)
|
|
||||||
+z: (int)
|
|
||||||
+z: (int)
|
|
||||||
|
|
||||||
|
|
||||||
-z: (
|
|
||||||
- Loooooooooooooooooooooooong
|
|
||||||
- | Loooooooooooooooooooooooong
|
|
||||||
- | Loooooooooooooooooooooooong
|
|
||||||
- | Loooooooooooooooooooooooong
|
|
||||||
-) = 7
|
|
||||||
+z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7
|
|
||||||
z: Short | Short2 | Short3 | Short4 = 8
|
|
||||||
z: int = 2.3
|
|
||||||
z: int = foo()
|
|
||||||
@@ -63,7 +53,7 @@
|
|
||||||
|
|
||||||
|
|
||||||
# remove unnecessary paren
|
# remove unnecessary paren
|
||||||
|
@ -133,7 +104,7 @@ def f(
|
||||||
|
|
||||||
|
|
||||||
# this is a syntax error in the type annotation according to mypy, but it's not invalid *python* code, so make sure we don't mess with it and make it so.
|
# this is a syntax error in the type annotation according to mypy, but it's not invalid *python* code, so make sure we don't mess with it and make it so.
|
||||||
@@ -72,12 +62,10 @@
|
@@ -72,12 +72,10 @@
|
||||||
|
|
||||||
def foo(
|
def foo(
|
||||||
i: int,
|
i: int,
|
||||||
|
@ -150,7 +121,7 @@ def f(
|
||||||
*,
|
*,
|
||||||
s: str,
|
s: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -88,7 +76,7 @@
|
@@ -88,7 +86,7 @@
|
||||||
async def foo(
|
async def foo(
|
||||||
q: str | None = Query(
|
q: str | None = Query(
|
||||||
None, title="Some long title", description="Some long description"
|
None, title="Some long title", description="Some long description"
|
||||||
|
@ -173,13 +144,23 @@ z = (
|
||||||
)
|
)
|
||||||
|
|
||||||
# "AnnAssign"s now also work
|
# "AnnAssign"s now also work
|
||||||
z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong
|
z: (
|
||||||
z: (Short | Short2 | Short3 | Short4)
|
Loooooooooooooooooooooooong
|
||||||
z: (int)
|
| Loooooooooooooooooooooooong
|
||||||
z: (int)
|
| Loooooooooooooooooooooooong
|
||||||
|
| Loooooooooooooooooooooooong
|
||||||
|
)
|
||||||
|
z: Short | Short2 | Short3 | Short4
|
||||||
|
z: int
|
||||||
|
z: int
|
||||||
|
|
||||||
|
|
||||||
z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7
|
z: (
|
||||||
|
Loooooooooooooooooooooooong
|
||||||
|
| Loooooooooooooooooooooooong
|
||||||
|
| Loooooooooooooooooooooooong
|
||||||
|
| Loooooooooooooooooooooooong
|
||||||
|
) = 7
|
||||||
z: Short | Short2 | Short3 | Short4 = 8
|
z: Short | Short2 | Short3 | Short4 = 8
|
||||||
z: int = 2.3
|
z: int = 2.3
|
||||||
z: int = foo()
|
z: int = foo()
|
||||||
|
|
|
@ -84,6 +84,16 @@ class DefaultRunner:
|
||||||
|
|
||||||
JSONSerializable: TypeAlias = (
|
JSONSerializable: TypeAlias = (
|
||||||
"str | int | float | bool | None | list | tuple | JSONMapping"
|
"str | int | float | bool | None | list | tuple | JSONMapping"
|
||||||
|
@@ -29,6 +29,6 @@
|
||||||
|
|
||||||
|
# Regression test: Don't forget the parentheses in the annotation when breaking
|
||||||
|
class DefaultRunner:
|
||||||
|
- task_runner_cls: TaskRunnerProtocol | typing.Callable[
|
||||||
|
- [], typing.Any
|
||||||
|
- ] = DefaultTaskRunner
|
||||||
|
+ task_runner_cls: TaskRunnerProtocol | typing.Callable[[], typing.Any] = (
|
||||||
|
+ DefaultTaskRunner
|
||||||
|
+ )
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,451 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/long_type_annotations.py
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```python
|
||||||
|
x1: A[b] | EventHandler | EventSpec | list[EventHandler | EventSpec] | Other | More | AndMore | None = None
|
||||||
|
|
||||||
|
x2: "VeryLongClassNameWithAwkwardGenericSubtype[int] |" "VeryLongClassNameWithAwkwardGenericSubtype[str]"
|
||||||
|
|
||||||
|
x6: VeryLongClassNameWithAwkwardGenericSubtype[
|
||||||
|
integeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer,
|
||||||
|
VeryLongClassNameWithAwkwardGenericSubtype,
|
||||||
|
str
|
||||||
|
] = True
|
||||||
|
|
||||||
|
|
||||||
|
x7: CustomTrainingJob | CustomContainerTrainingJob | CustomPythonPackageTrainingJob
|
||||||
|
x8: (
|
||||||
|
None
|
||||||
|
| datasets.ImageDataset
|
||||||
|
| datasets.TabularDataset
|
||||||
|
| datasets.TextDataset
|
||||||
|
| datasets.VideoDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
x9: None | (
|
||||||
|
datasets.ImageDataset
|
||||||
|
| datasets.TabularDataset
|
||||||
|
| datasets.TextDataset
|
||||||
|
| datasets.VideoDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
|
||||||
|
x10: (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaa[
|
||||||
|
bbbbbbbbbbb,
|
||||||
|
Subscript
|
||||||
|
| None
|
||||||
|
| datasets.ImageDataset
|
||||||
|
| datasets.TabularDataset
|
||||||
|
| datasets.TextDataset
|
||||||
|
| datasets.VideoDataset,
|
||||||
|
],
|
||||||
|
bbb[other],
|
||||||
|
) = None
|
||||||
|
|
||||||
|
x11: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] = None
|
||||||
|
|
||||||
|
x12: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | Other = None
|
||||||
|
|
||||||
|
|
||||||
|
x13: [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | Other = None
|
||||||
|
|
||||||
|
x14: [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] = None
|
||||||
|
|
||||||
|
x15: [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | Other = None
|
||||||
|
|
||||||
|
x16: None | Literal[
|
||||||
|
"split",
|
||||||
|
"a bit longer",
|
||||||
|
"records",
|
||||||
|
"index",
|
||||||
|
"table",
|
||||||
|
"columns",
|
||||||
|
"values",
|
||||||
|
] = None
|
||||||
|
|
||||||
|
x17: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Test:
|
||||||
|
safe_age: Decimal # the user's age, used to determine if it's safe for them to use ruff
|
||||||
|
applied_fixes: int # the number of fixes that this user applied. Used for ranking the users with the most applied fixes.
|
||||||
|
string_annotation: "Test" # a long comment after a quoted, runtime-only type annotation
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Comments
|
||||||
|
|
||||||
|
leading: (
|
||||||
|
# Leading comment
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
)
|
||||||
|
|
||||||
|
leading_with_value: (
|
||||||
|
# Leading comment
|
||||||
|
None
|
||||||
|
| dataset.ImageDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
leading_open_parentheses: ( # Leading comment
|
||||||
|
None
|
||||||
|
| dataset.ImageDataset
|
||||||
|
)
|
||||||
|
|
||||||
|
leading_open_parentheses_with_value: ( # Leading comment
|
||||||
|
None
|
||||||
|
| dataset.ImageDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
trailing: (
|
||||||
|
None | dataset.ImageDataset # trailing comment
|
||||||
|
)
|
||||||
|
|
||||||
|
trailing_with_value: (
|
||||||
|
None | dataset.ImageDataset # trailing comment
|
||||||
|
) = None
|
||||||
|
|
||||||
|
trailing_own_line: (
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
# trailing own line
|
||||||
|
)
|
||||||
|
|
||||||
|
trailing_own_line_with_value: (
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
# trailing own line
|
||||||
|
) = None
|
||||||
|
|
||||||
|
nested_comment: None | [
|
||||||
|
# a list of strings
|
||||||
|
str
|
||||||
|
] = None
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
```python
|
||||||
|
x1: A[b] | EventHandler | EventSpec | list[
|
||||||
|
EventHandler | EventSpec
|
||||||
|
] | Other | More | AndMore | None = None
|
||||||
|
|
||||||
|
x2: "VeryLongClassNameWithAwkwardGenericSubtype[int] |" "VeryLongClassNameWithAwkwardGenericSubtype[str]"
|
||||||
|
|
||||||
|
x6: VeryLongClassNameWithAwkwardGenericSubtype[
|
||||||
|
integeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer,
|
||||||
|
VeryLongClassNameWithAwkwardGenericSubtype,
|
||||||
|
str,
|
||||||
|
] = True
|
||||||
|
|
||||||
|
|
||||||
|
x7: CustomTrainingJob | CustomContainerTrainingJob | CustomPythonPackageTrainingJob
|
||||||
|
x8: (
|
||||||
|
None
|
||||||
|
| datasets.ImageDataset
|
||||||
|
| datasets.TabularDataset
|
||||||
|
| datasets.TextDataset
|
||||||
|
| datasets.VideoDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
x9: None | (
|
||||||
|
datasets.ImageDataset
|
||||||
|
| datasets.TabularDataset
|
||||||
|
| datasets.TextDataset
|
||||||
|
| datasets.VideoDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
|
||||||
|
x10: (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaa[
|
||||||
|
bbbbbbbbbbb,
|
||||||
|
Subscript
|
||||||
|
| None
|
||||||
|
| datasets.ImageDataset
|
||||||
|
| datasets.TabularDataset
|
||||||
|
| datasets.TextDataset
|
||||||
|
| datasets.VideoDataset,
|
||||||
|
],
|
||||||
|
bbb[other],
|
||||||
|
) = None
|
||||||
|
|
||||||
|
x11: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] = None
|
||||||
|
|
||||||
|
x12: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | Other = None
|
||||||
|
|
||||||
|
|
||||||
|
x13: [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | Other = None
|
||||||
|
|
||||||
|
x14: [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] = None
|
||||||
|
|
||||||
|
x15: [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] | Other = None
|
||||||
|
|
||||||
|
x16: None | Literal[
|
||||||
|
"split",
|
||||||
|
"a bit longer",
|
||||||
|
"records",
|
||||||
|
"index",
|
||||||
|
"table",
|
||||||
|
"columns",
|
||||||
|
"values",
|
||||||
|
] = None
|
||||||
|
|
||||||
|
x17: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
datasets.TabularDataset,
|
||||||
|
datasets.TextDataset,
|
||||||
|
datasets.VideoDataset,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Test:
|
||||||
|
safe_age: Decimal # the user's age, used to determine if it's safe for them to use ruff
|
||||||
|
applied_fixes: int # the number of fixes that this user applied. Used for ranking the users with the most applied fixes.
|
||||||
|
string_annotation: "Test" # a long comment after a quoted, runtime-only type annotation
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Comments
|
||||||
|
|
||||||
|
leading: (
|
||||||
|
# Leading comment
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
)
|
||||||
|
|
||||||
|
leading_with_value: (
|
||||||
|
# Leading comment
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
leading_open_parentheses: ( # Leading comment
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
)
|
||||||
|
|
||||||
|
leading_open_parentheses_with_value: ( # Leading comment
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
) = None
|
||||||
|
|
||||||
|
trailing: (
|
||||||
|
None | dataset.ImageDataset # trailing comment
|
||||||
|
)
|
||||||
|
|
||||||
|
trailing_with_value: (
|
||||||
|
None | dataset.ImageDataset # trailing comment
|
||||||
|
) = None
|
||||||
|
|
||||||
|
trailing_own_line: (
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
# trailing own line
|
||||||
|
)
|
||||||
|
|
||||||
|
trailing_own_line_with_value: (
|
||||||
|
None | dataset.ImageDataset
|
||||||
|
# trailing own line
|
||||||
|
) = None
|
||||||
|
|
||||||
|
nested_comment: None | [
|
||||||
|
# a list of strings
|
||||||
|
str
|
||||||
|
] = None
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -1,8 +1,18 @@
|
||||||
|
-x1: A[b] | EventHandler | EventSpec | list[
|
||||||
|
- EventHandler | EventSpec
|
||||||
|
-] | Other | More | AndMore | None = None
|
||||||
|
+x1: (
|
||||||
|
+ A[b]
|
||||||
|
+ | EventHandler
|
||||||
|
+ | EventSpec
|
||||||
|
+ | list[EventHandler | EventSpec]
|
||||||
|
+ | Other
|
||||||
|
+ | More
|
||||||
|
+ | AndMore
|
||||||
|
+ | None
|
||||||
|
+) = None
|
||||||
|
|
||||||
|
-x2: "VeryLongClassNameWithAwkwardGenericSubtype[int] |" "VeryLongClassNameWithAwkwardGenericSubtype[str]"
|
||||||
|
+x2: (
|
||||||
|
+ "VeryLongClassNameWithAwkwardGenericSubtype[int] |"
|
||||||
|
+ "VeryLongClassNameWithAwkwardGenericSubtype[str]"
|
||||||
|
+)
|
||||||
|
|
||||||
|
x6: VeryLongClassNameWithAwkwardGenericSubtype[
|
||||||
|
integeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer,
|
||||||
|
@@ -48,12 +58,16 @@
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] = None
|
||||||
|
|
||||||
|
-x12: None | [
|
||||||
|
- datasets.ImageDataset,
|
||||||
|
- datasets.TabularDataset,
|
||||||
|
- datasets.TextDataset,
|
||||||
|
- datasets.VideoDataset,
|
||||||
|
-] | Other = None
|
||||||
|
+x12: (
|
||||||
|
+ None
|
||||||
|
+ | [
|
||||||
|
+ datasets.ImageDataset,
|
||||||
|
+ datasets.TabularDataset,
|
||||||
|
+ datasets.TextDataset,
|
||||||
|
+ datasets.VideoDataset,
|
||||||
|
+ ]
|
||||||
|
+ | Other
|
||||||
|
+) = None
|
||||||
|
|
||||||
|
|
||||||
|
x13: [
|
||||||
|
@@ -75,27 +89,34 @@
|
||||||
|
datasets.VideoDataset,
|
||||||
|
] = None
|
||||||
|
|
||||||
|
-x15: [
|
||||||
|
- datasets.ImageDataset,
|
||||||
|
- datasets.TabularDataset,
|
||||||
|
- datasets.TextDataset,
|
||||||
|
- datasets.VideoDataset,
|
||||||
|
-] | [
|
||||||
|
- datasets.ImageDataset,
|
||||||
|
- datasets.TabularDataset,
|
||||||
|
- datasets.TextDataset,
|
||||||
|
- datasets.VideoDataset,
|
||||||
|
-] | Other = None
|
||||||
|
+x15: (
|
||||||
|
+ [
|
||||||
|
+ datasets.ImageDataset,
|
||||||
|
+ datasets.TabularDataset,
|
||||||
|
+ datasets.TextDataset,
|
||||||
|
+ datasets.VideoDataset,
|
||||||
|
+ ]
|
||||||
|
+ | [
|
||||||
|
+ datasets.ImageDataset,
|
||||||
|
+ datasets.TabularDataset,
|
||||||
|
+ datasets.TextDataset,
|
||||||
|
+ datasets.VideoDataset,
|
||||||
|
+ ]
|
||||||
|
+ | Other
|
||||||
|
+) = None
|
||||||
|
|
||||||
|
-x16: None | Literal[
|
||||||
|
- "split",
|
||||||
|
- "a bit longer",
|
||||||
|
- "records",
|
||||||
|
- "index",
|
||||||
|
- "table",
|
||||||
|
- "columns",
|
||||||
|
- "values",
|
||||||
|
-] = None
|
||||||
|
+x16: (
|
||||||
|
+ None
|
||||||
|
+ | Literal[
|
||||||
|
+ "split",
|
||||||
|
+ "a bit longer",
|
||||||
|
+ "records",
|
||||||
|
+ "index",
|
||||||
|
+ "table",
|
||||||
|
+ "columns",
|
||||||
|
+ "values",
|
||||||
|
+ ]
|
||||||
|
+) = None
|
||||||
|
|
||||||
|
x17: None | [
|
||||||
|
datasets.ImageDataset,
|
||||||
|
@@ -106,9 +127,13 @@
|
||||||
|
|
||||||
|
|
||||||
|
class Test:
|
||||||
|
- safe_age: Decimal # the user's age, used to determine if it's safe for them to use ruff
|
||||||
|
+ safe_age: (
|
||||||
|
+ Decimal # the user's age, used to determine if it's safe for them to use ruff
|
||||||
|
+ )
|
||||||
|
applied_fixes: int # the number of fixes that this user applied. Used for ranking the users with the most applied fixes.
|
||||||
|
- string_annotation: "Test" # a long comment after a quoted, runtime-only type annotation
|
||||||
|
+ string_annotation: (
|
||||||
|
+ "Test" # a long comment after a quoted, runtime-only type annotation
|
||||||
|
+ )
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue