From c3b506fca6cd03a434e0974c66d0bf4b345dd039 Mon Sep 17 00:00:00 2001 From: konsti Date: Fri, 21 Jul 2023 11:32:35 +0200 Subject: [PATCH] Add script to shrink all formatter errors (#5943) **Summary** Add script to shrink all formatter errors: This started as a fun idea and turned out really useful: This script gives us a single Python file with all formatter stability errors. I want to keep it around to occasionally update #5828 so I added it to the git. **Test Plan** None, this is a helper script --- .../shrink_formatter_errors.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 crates/ruff_python_formatter/shrink_formatter_errors.py diff --git a/crates/ruff_python_formatter/shrink_formatter_errors.py b/crates/ruff_python_formatter/shrink_formatter_errors.py new file mode 100644 index 0000000000000..3d104b7c0317e --- /dev/null +++ b/crates/ruff_python_formatter/shrink_formatter_errors.py @@ -0,0 +1,75 @@ +"""Take `format-dev --stability-check` output and shrink all stability errors into a +single Python file. Used to update https://github.com/astral-sh/ruff/issues/5828 .""" + +from __future__ import annotations + +import json +import os +from concurrent.futures import ThreadPoolExecutor, as_completed +from pathlib import Path +from subprocess import check_output +from tempfile import NamedTemporaryFile + +from tqdm import tqdm + +root = Path( + check_output(["git", "rev-parse", "--show-toplevel"], text=True).strip(), +) +target = root.joinpath("target") + +error_report = target.joinpath("formatter-ecosystem-errors.txt") +error_lines_prefix = "Unstable formatting " + + +def get_filenames() -> list[str]: + files = [] + for line in error_report.read_text().splitlines(): + if not line.startswith(error_lines_prefix): + continue + files.append(line.removeprefix(error_lines_prefix)) + return files + + +def shrink_file(file: str) -> tuple[str, str]: + """Returns filename and minimization""" + with NamedTemporaryFile(suffix=".py") as temp_file: + print(f"Starting {file}") + ruff_dev = target.joinpath("release").joinpath("ruff_dev") + check_output( + [ + target.joinpath("release").joinpath("ruff_shrinking"), + file, + temp_file.name, + "Unstable formatting", + f"{ruff_dev} format-dev --stability-check {temp_file.name}", + ], + ) + print(f"Finished {file}") + return file, Path(temp_file.name).read_text() + + +def main(): + storage = target.joinpath("minimizations.json") + output_file = target.joinpath("minimizations.py") + if storage.is_file(): + outputs = json.loads(storage.read_text()) + else: + outputs = {} + files = sorted(set(get_filenames()) - set(outputs)) + # Each process will saturate one core + with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: + tasks = [executor.submit(shrink_file, file) for file in files] + for future in tqdm(as_completed(tasks), total=len(files)): + file, output = future.result() + outputs[file] = output + storage.write_text(json.dumps(outputs, indent=4)) + + # Write to one shareable python file + with output_file.open("w") as formatted: + for file, code in sorted(json.loads(storage.read_text()).items()): + file = file.split("/target/checkouts/")[1] + formatted.write(f"# {file}\n{code}\n") + + +if __name__ == "__main__": + main()