-
-
Save charliermarsh/3f2855f33960c92306c91d003b6237dc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import annotations | |
import os | |
import subprocess | |
SELECT = [ | |
"E111", | |
"E112", | |
"E113", | |
"E114", | |
"E115", | |
"E116", | |
"E117", | |
"E201", | |
"E202", | |
"E203", | |
"E211", | |
"E221", | |
"E222", | |
"E223", | |
"E224", | |
"E225", | |
"E226", | |
"E227", | |
"E228", | |
"E231", | |
"E251", | |
"E252", | |
"E261", | |
"E262", | |
"E265", | |
"E266", | |
"E271", | |
"E272", | |
"E273", | |
"E274", | |
"E275", | |
] | |
def take_after(s: str) -> str: | |
"""Given filename:204:22, return everything after the first colon.""" | |
s = s.split(":", 1)[1] | |
s = s.replace( | |
"`# `", | |
"'# '", | |
) | |
s = s.replace( | |
"`#`", | |
"'#'", | |
) | |
s = s.replace( | |
"for block comment", | |
"before block comment", | |
) | |
s = s.replace( | |
"Insert at least two spaces before an inline comment", | |
"at least two spaces before inline comment", | |
) | |
return s | |
def take_comparable(s: str) -> str: | |
s = s.lower() | |
# Ignore location, I guess? The issue here is really for "Over-indented (comment") | |
# where we use the start of the indentation, but pycodestyle uses the start of the | |
# comment. | |
if "over-indented" in s: | |
s = s.split(":", 2)[-1] | |
return s | |
def case_insensitive_compare(a: list[str], b: list[str]) -> bool: | |
return [take_comparable(line) for line in a] == [ | |
take_comparable(line) for line in b | |
] | |
def pycodestyle(fname: str) -> list[str]: | |
output = subprocess.getoutput(f"pycodestyle {fname} --select {','.join(SELECT)}") | |
if output: | |
return [take_after(line) for line in output.split("\n") if ":" in line] | |
else: | |
return [] | |
def ruff(fname: str) -> list[str]: | |
output = subprocess.getoutput( | |
f"./target/debug/ruff {fname} --select {','.join(SELECT)} --no-cache" | |
) | |
if output: | |
return [take_after(line) for line in output.split("\n") if ":" in line] | |
else: | |
return [] | |
if __name__ == "__main__": | |
dirname = "../scipy/scipy" | |
# Walk over every file in the directory. | |
seen = False | |
for root, dirs, files in os.walk(dirname): | |
for filename in files: | |
# If the file is a python file, check if it contains a docstring. | |
if filename.endswith(".py"): | |
filepath = os.path.join(root, filename) | |
# Local optimization: skip until you see a specific failing file... | |
if False: | |
if filepath == "../scipy/scipy/stats/tests/__init__.py": | |
seen = True | |
if not seen: | |
continue | |
# Run pycodestyle on the file. | |
pycodestyle_errors = pycodestyle(filepath) | |
ruff_errors = ruff(filepath) | |
if case_insensitive_compare(pycodestyle_errors, ruff_errors): | |
print(f"OK: {filepath}") | |
else: | |
print(f"File: {filepath}") | |
# Print the first error that differs. | |
for i in range(min(len(pycodestyle_errors), len(ruff_errors))): | |
if pycodestyle_errors[i].lower() != ruff_errors[i].lower(): | |
print(f"pycodestyle: {pycodestyle_errors[i]}") | |
print(f"ruff: {ruff_errors[i]}") | |
break | |
# Print the remaining errors. | |
if len(pycodestyle_errors) > len(ruff_errors): | |
for error in pycodestyle_errors[len(ruff_errors) :]: | |
print(f"pycodestyle: {error}") | |
else: | |
for error in ruff_errors[len(pycodestyle_errors) :]: | |
print(f"ruff: {error}") | |
exit(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment