1
1
use ruff_formatter:: { format_args, write, FormatError } ;
2
- use ruff_python_ast:: { AnyNodeRef , Expr , Operator , StmtAssign , TypeParams } ;
2
+ use ruff_python_ast:: {
3
+ AnyNodeRef , Expr , ExprAttribute , ExprCall , Operator , StmtAssign , TypeParams ,
4
+ } ;
3
5
4
6
use crate :: builders:: parenthesize_if_expands;
5
7
use crate :: comments:: {
@@ -9,7 +11,7 @@ use crate::context::{NodeLevel, WithNodeLevel};
9
11
use crate :: expression:: parentheses:: {
10
12
is_expression_parenthesized, NeedsParentheses , OptionalParentheses , Parentheses , Parenthesize ,
11
13
} ;
12
- use crate :: expression:: { has_own_parentheses, maybe_parenthesize_expression} ;
14
+ use crate :: expression:: { has_own_parentheses, has_parentheses , maybe_parenthesize_expression} ;
13
15
use crate :: prelude:: * ;
14
16
use crate :: preview:: is_prefer_splitting_right_hand_side_of_assignments_enabled;
15
17
use crate :: statement:: trailing_semicolon;
@@ -56,7 +58,7 @@ impl FormatNodeRule<StmtAssign> for FormatStmtAssign {
56
58
}
57
59
. fmt ( f) ?;
58
60
}
59
- // Avoid parenthesizing the value for single-target assignments that where the
61
+ // Avoid parenthesizing the value for single-target assignments where the
60
62
// target has its own parentheses (list, dict, tuple, ...) and the target expands.
61
63
else if has_target_own_parentheses ( first, f. context ( ) )
62
64
&& !is_expression_parenthesized (
@@ -193,14 +195,14 @@ impl Format<PyFormatContext<'_>> for FormatTargetWithEqualOperator<'_> {
193
195
|| f. context ( ) . comments ( ) . has_trailing ( self . target )
194
196
{
195
197
self . target . format ( ) . fmt ( f) ?;
196
- } else if has_target_own_parentheses ( self . target , f. context ( ) ) {
198
+ } else if should_parenthesize_target ( self . target , f. context ( ) ) {
199
+ parenthesize_if_expands ( & self . target . format ( ) . with_options ( Parentheses :: Never ) )
200
+ . fmt ( f) ?;
201
+ } else {
197
202
self . target
198
203
. format ( )
199
204
. with_options ( Parentheses :: Never )
200
205
. fmt ( f) ?;
201
- } else {
202
- parenthesize_if_expands ( & self . target . format ( ) . with_options ( Parentheses :: Never ) )
203
- . fmt ( f) ?;
204
206
}
205
207
206
208
write ! ( f, [ space( ) , token( "=" ) , space( ) ] )
@@ -554,7 +556,9 @@ impl Format<PyFormatContext<'_>> for FormatStatementsLastExpression<'_> {
554
556
555
557
// For call expressions, prefer breaking after the call expression's opening parentheses
556
558
// over parenthesizing the entire call expression.
557
- if value. is_call_expr ( ) {
559
+ // For subscripts, try breaking the subscript first
560
+ // For attribute chains that contain any parenthesized value: Try expanding the parenthesized value first.
561
+ if value. is_call_expr ( ) || value. is_subscript_expr ( ) || value. is_attribute_expr ( ) {
558
562
best_fitting ! [
559
563
format_flat,
560
564
// Avoid parenthesizing the call expression if the `(` fit on the line
@@ -681,11 +685,11 @@ impl Format<PyFormatContext<'_>> for AnyBeforeOperator<'_> {
681
685
. fmt ( f)
682
686
}
683
687
// Never parenthesize targets that come with their own parentheses, e.g. don't parenthesize lists or dictionary literals.
684
- else if has_target_own_parentheses ( expression, f. context ( ) ) {
685
- expression. format ( ) . with_options ( Parentheses :: Never ) . fmt ( f)
686
- } else {
688
+ else if should_parenthesize_target ( expression, f. context ( ) ) {
687
689
parenthesize_if_expands ( & expression. format ( ) . with_options ( Parentheses :: Never ) )
688
690
. fmt ( f)
691
+ } else {
692
+ expression. format ( ) . with_options ( Parentheses :: Never ) . fmt ( f)
689
693
}
690
694
}
691
695
// Never parenthesize type params
@@ -717,7 +721,7 @@ fn should_inline_comments(
717
721
}
718
722
}
719
723
720
- /// Tests whether an expression that for which comments shouldn't be inlined should use the best fit layout
724
+ /// Tests whether an expression for which comments shouldn't be inlined should use the best fit layout
721
725
fn should_non_inlineable_use_best_fit (
722
726
expr : & Expr ,
723
727
parent : AnyNodeRef ,
@@ -728,12 +732,32 @@ fn should_non_inlineable_use_best_fit(
728
732
attribute. needs_parentheses ( parent, context) == OptionalParentheses :: BestFit
729
733
}
730
734
Expr :: Call ( call) => call. needs_parentheses ( parent, context) == OptionalParentheses :: BestFit ,
735
+ Expr :: Subscript ( subscript) => {
736
+ subscript. needs_parentheses ( parent, context) == OptionalParentheses :: BestFit
737
+ }
731
738
_ => false ,
732
739
}
733
740
}
734
741
735
- /// Returns `true` for targets that should not be parenthesized if they split because their expanded
736
- /// layout comes with their own set of parentheses .
742
+ /// Returns `true` for targets that have their own set of parentheses when they split,
743
+ /// in which case we want to avoid parenthesizing the assigned value .
737
744
pub ( super ) fn has_target_own_parentheses ( target : & Expr , context : & PyFormatContext ) -> bool {
738
745
matches ! ( target, Expr :: Tuple ( _) ) || has_own_parentheses ( target, context) . is_some ( )
739
746
}
747
+
748
+ pub ( super ) fn should_parenthesize_target ( target : & Expr , context : & PyFormatContext ) -> bool {
749
+ !( has_target_own_parentheses ( target, context)
750
+ || is_attribute_with_parenthesized_value ( target, context) )
751
+ }
752
+
753
+ fn is_attribute_with_parenthesized_value ( target : & Expr , context : & PyFormatContext ) -> bool {
754
+ match target {
755
+ Expr :: Attribute ( ExprAttribute { value, .. } ) => {
756
+ has_parentheses ( value. as_ref ( ) , context) . is_some ( )
757
+ || is_attribute_with_parenthesized_value ( value, context)
758
+ }
759
+ Expr :: Subscript ( _) => true ,
760
+ Expr :: Call ( ExprCall { arguments, .. } ) => !arguments. is_empty ( ) ,
761
+ _ => false ,
762
+ }
763
+ }
0 commit comments