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

Run second pass of formatter #2549

Closed
wants to merge 1 commit into from
Closed

Conversation

nipunn1313
Copy link
Contributor

Description

Run the formatter twice to ensure stability.
Fixes #2488 and #2518

Admittedly, it doesn't fix the underlying issue. There are three
expected-failure tests in test_black.py, which prove the issue
(test_trailing_comma_optional_parens_stability{1,2,3})

This is a (somewhat terrible) bandaid which runs the entire formatter
twice to work around this problem. This bandaid actually already existed
in the stability-checking-validation code, but resulted in a non-stable
black executable. The problem appears preexisting and known
based on the disabled tests and the comments, so this feels like a
reasonable step-in-the-right-direction.

Follow up tasks may want to consider removing this hack and actually
fixing the underlying bug that forces us to run the formatter twice. I tried
to dig into it, but it was pretty tough to figure out. There are some notes in
#2488. The result of can_omit_invisible_parens changes during pass1, causing
pass2 to do new work that wasn't considered in pass1.

Checklist - did you ...

  • [x ] Add a CHANGELOG entry if necessary?
  • [x ] Add / update tests if necessary?
  • [x ] Add new / update outdated documentation?

@JelleZijlstra
Copy link
Collaborator

We already run the formatter twice to address stability issues with the magic trailing comma, so this might be too much.

@@ -47,6 +47,9 @@ def _assert_format_equal(expected: str, actual: str) -> None:
except Exception as ve:
err(str(ve))

if actual != expected:
out(diff(expected, actual, "expected", "actual"))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I found this incredibly helpful in debugging the test failures.
It already prints out the ASTs, but for some of the test files, the ASTs are huge. This prints a diff of the actual output as well.

@nipunn1313
Copy link
Contributor Author

No, we don't (as far as I can tell). That's what this diff addresses.

@nipunn1313
Copy link
Contributor Author

Sorry. Let me elaborate.

I found that we (previously) ran the formatter twice in the validation phase, but not in the actual runtime.

Thus the tests passed (since the formatter ran twice), but the actual black executable still had the stability issues.

This diff moves the double-execution to the runtime, rather than in the check.

@onerandomusername
Copy link
Contributor

TBH, I'm not sure I fully understand what the fix here is, and the change to consumers. Would black still take the same amount of time to run?

@nipunn1313
Copy link
Contributor Author

nipunn1313 commented Oct 20, 2021

Left some clarifying comments inline in the diff to help clarify what has changed.

Difference to consumers is that it's actually stable w.r.t. magic trailing comma. See bug #2518. Previously consumers would have to manually run black twice to work around the bug and achieve correctness.

@@ -914,13 +912,17 @@ def format_file_contents(src_contents: str, *, fast: bool, mode: Mode) -> FileCo
if mode.is_ipynb:
dst_contents = format_ipynb_string(src_contents, fast=fast, mode=mode)
else:
dst_contents = format_str(src_contents, mode=mode)
dst_contents_pass1 = format_str(src_contents, mode=mode)
dst_contents = format_str(dst_contents_pass1, mode=mode)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This actually runs the second pass of the formatter and returns the results of the second pass out

# forced trailing commas on pass 2) interacting differently with optional
# parentheses. Admittedly ugly.
dst_contents_pass2 = format_str(dst_contents, mode=mode)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously, the second pass only happened here in the check_stability_and_equivalence method, and thus wasn't actually being written out / returned from format_file_contents.

Fixes psf#2488 and psf#2518

Admittedly, it doesn't fix the underlying issue. There are three
expected-failure tests in test_black.py, which prove the issue
(test_trailing_comma_optional_parens_stability{1,2,3})

This is a (somewhat terrible) bandaid which runs the entire formatter
twice to work around this problem. The problem appears preexisting
based on the disabled tests and the comments, so this feels like a
reasonable step-in-the-right-direction.
@felix-hilden
Copy link
Collaborator

felix-hilden commented Oct 20, 2021

I tend to agree with Jelle. I don't care for bandaid solutions unless really necessary. Having a second pass on validation is good user experience, but this seems really icky since running it twice could potentially obscure even more bugs. Haven't reviewed the code thoroughly yet though, so I might have misunderstood something.

@nipunn1313
Copy link
Contributor Author

I'm with all of you. I too don't care for bandaid solutions.

Currently, there's a bandaid in the validation step (running a second pass before checking stability) which is currently obscuring real stability bugs (#2518). The testsuite would have caught this bug without the validation bandaid.

As long as we're running two passes before checking stability in the testsuite, we will continue to obscure true stability bugs - and it's likely further stability regressions will be introduced.

Based on this discussion, I'd be in favor of an alternate proposal

  • Remove the second pass from the validation step
  • Mark/disable the existing failing tests.
  • Prioritize fixing the failing tests (eg Non idempotent input #2518)

I did spend several hours actually trying to actually fix the stability issue, but it was rather difficult to figure out exactly how to solve it. The first pass would omit parens while adding a trailing comma. The second pass would refuse to omit parens because of the trailing comma. Granted I am inexperienced w/ the codebase - maybe someone else will have more success.

@ichard26 ichard26 self-requested a review October 21, 2021 12:25
@nipunn1313
Copy link
Contributor Author

I took another go at it and actually was able to come up with a possible solution to the underlying problem in #2572! Guess I had to sleep on it for a week.

@JelleZijlstra
Copy link
Collaborator

Thanks for this, and sorry for not giving it the attention it deserved! I pushed a separate change to run formatting twice, but am now leaning towards removing it again with your #2572 change.

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.

black -C and black disagrees on some formatting
5 participants