Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefer more equal signs before a break when splitting chained assignments #4010

Merged
merged 19 commits into from Nov 23, 2023

Conversation

henriholopainen
Copy link
Contributor

@henriholopainen henriholopainen commented Oct 31, 2023

Description

This PR makes rhs processing prefer more equal signs before breaking the line.

Fixes #4007

Checklist - did you ...

  • Add an entry in CHANGES.md if necessary?
  • Add / update tests if necessary?

Copy link

github-actions bot commented Oct 31, 2023

diff-shades results comparing this PR (4ad7c39) to main (be336bb). The full diff is available in the logs under the "Generate HTML diff report" step.

╭─────────────────────── Summary ────────────────────────╮
│ 2 projects & 19 files changed / 176 changes [+86/-90]  │
│                                                        │
│ ... out of 2 522 686 lines, 11 793 files & 23 projects │
╰────────────────────────────────────────────────────────╯

Differences found.

What is this? | Workflow run | diff-shades documentation

@MichaReiser
Copy link

MichaReiser commented Nov 2, 2023

Thanks

The following diff seems interesting

         elif constraint is None:
-            self.constraint_target = (
-                self.inferred_target_elements
-            ) = self.inferred_target_whereclause = None
+            self.constraint_target = self.inferred_target_elements = (
+                self.inferred_target_whereclause
+            ) = None

But I guess there's not much we can do about (we need the parentheses because it's python)

src/black/linegen.py Outdated Show resolved Hide resolved
@JelleZijlstra
Copy link
Collaborator

I'm not quite sure about this. Looking at the diff-shades output, there's a few good ones:

-                            collected_attributes[name] = column_copies[
-                                obj
-                            ] = ret
+                            collected_attributes[name] = column_copies[obj] = (
+                                ret
+                            )

It's nicer to keep the subscript on one line.

-        self.selectable = (
-            self.persist_selectable
-        ) = self.local_table = selectable
+        self.selectable = self.persist_selectable = self.local_table = (
+            selectable
+        )

It's better to keep all the assignment targets next to each other, instead of randomly enclosing one in parentheses.

But there's also a lot of cases like the one @MichaReiser highlighted, where we just change which of the LHS assignment targets get parenthesized. In those cases, I don't think either formatting is clearly better; it looks ugly either way. I'd rather have Black not make a change there, because every time we change the formatting of existing code, that's disruptive to users. I'd want to make changes only if we believe the new formatting is better. Therefore, I'd prefer to stick with the current formatting in those cases.

Perhaps the rule could be that we split around the RHS if possible, and if that doesn't work, we preserve the current behavior and split around the second assignment target from the left.

@henriholopainen
Copy link
Contributor Author

I'm not quite sure about this. Looking at the diff-shades output, there's a few good ones:

-                            collected_attributes[name] = column_copies[
-                                obj
-                            ] = ret
+                            collected_attributes[name] = column_copies[obj] = (
+                                ret
+                            )

It's nicer to keep the subscript on one line.

-        self.selectable = (
-            self.persist_selectable
-        ) = self.local_table = selectable
+        self.selectable = self.persist_selectable = self.local_table = (
+            selectable
+        )

It's better to keep all the assignment targets next to each other, instead of randomly enclosing one in parentheses.

But there's also a lot of cases like the one @MichaReiser highlighted, where we just change which of the LHS assignment targets get parenthesized. In those cases, I don't think either formatting is clearly better; it looks ugly either way. I'd rather have Black not make a change there, because every time we change the formatting of existing code, that's disruptive to users. I'd want to make changes only if we believe the new formatting is better. Therefore, I'd prefer to stick with the current formatting in those cases.

Perhaps the rule could be that we split around the RHS if possible, and if that doesn't work, we preserve the current behavior and split around the second assignment target from the left.

For me even @MichaReiser's example is better with the new formatting. Somehow it feels more natural when as many assignments as possible end up formatted as early as possible. Consider for example this (rather extreme) example:

self.selectable = self.persist_selectable = self.local_table = self.foo_bar = self.foo = self.longlonglong_key = self.another_long_long_long_key = self.yet_again_a_long_key = value

On current main it becomes:

self.selectable = (
    self.persist_selectable
) = (
    self.local_table
) = (
    self.foo_bar
) = (
    self.foo
) = (
    self.longlonglong_key
) = self.another_long_long_long_key = self.yet_again_a_long_key = value

and with this PR:

self.selectable = self.persist_selectable = self.local_table = self.foo_bar = (
    self.foo
) = self.longlonglong_key = self.another_long_long_long_key = (
    self.yet_again_a_long_key
) = value

I don't think a hybrid model would make sense here:

self.selectable = (
    self.persist_selectable
) = (
    self.local_table
) = (
    self.foo_bar
) = (
    self.foo
) =  self.longlonglong_key = self.another_long_long_long_key = self.yet_again_a_long_key = (
    value
)

I think there is some inherent value to consistency and easy to follow rules. Thus I feel like we should stick with either splitting early or late, but not make it depend on the case, and in general it seems splitting late >= splitting early. And while I do appreciate avoiding introducing unnecessary diffs, this is quite the corner case and doesn't touch that many lines of code.

All that being said, as a contributor I trust your view on what is good and desired. Also, even though intuitively I feel consistency should weigh in cases where two formatting options are both ugly, I'm not fully sure if it should be enough to justify changing the formatting.

@JelleZijlstra
Copy link
Collaborator

@hauntsaninja do you have any opinion here? I think this is mostly an improvement but I'm a bit hesitant to make style changes in existing code that aren't clear improvements.

Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's an improvement! Splitting the one in the middle is not usually a choice a human would make.
Moreover, the change here isn't super common or controversial, so I think churn costs aren't too high.

@JelleZijlstra
Copy link
Collaborator

Sounds good! Unfortunately diff-shades failed again, I'll retry to see if it succeeds.

Copy link
Collaborator

@JelleZijlstra JelleZijlstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do it then, I'd like to see if I can get diff-shades to work first though.

@JelleZijlstra
Copy link
Collaborator

I think the diff-shades errors miraculously fixed themselves.

@JelleZijlstra JelleZijlstra merged commit fb5e5d2 into psf:main Nov 23, 2023
46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Preview: Improved line breaks with multiple targets
4 participants