mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
* scripts: make check_redundant_patches.py executable The redundant-test-patches pre-commit hook uses `language: script`, which requires the executable bit, and fails on every commit touching Lib/test. Assisted-by: Claude:claude-sonnet-5-5 * csv: support non-ASCII Unicode dialect characters Store delimiter, quotechar and escapechar as `char` instead of `u8`, and run the reader state machine and the writer over code points, so that a single non-ASCII character such as `€` is accepted like in CPython. The field size limit is now counted in characters rather than bytes. Fixes #8408 Assisted-by: Claude:claude-sonnet-5-5
93 lines
2.4 KiB
Python
Executable File
93 lines
2.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import argparse
|
|
import ast
|
|
import glob
|
|
import os
|
|
import pathlib
|
|
import sys
|
|
|
|
ROOT = pathlib.Path(__file__).parents[1]
|
|
TEST_DIR = ROOT / "Lib" / "test"
|
|
|
|
IS_GH_CI = "GITHUB_ACTIONS" in os.environ
|
|
|
|
|
|
def build_argparser() -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(prog="check_redundant_patches")
|
|
parser.add_argument(
|
|
"patterns",
|
|
nargs="*",
|
|
default=[f"{TEST_DIR}/**/*.py"],
|
|
help="Glob patterns (e.g. foo/bar/**.py a/b/file.py)",
|
|
)
|
|
|
|
return parser
|
|
|
|
|
|
def iter_files(patterns: list[str]):
|
|
seen = set()
|
|
for pattern in set(patterns):
|
|
matches = glob.glob(pattern, recursive=True)
|
|
for path in matches:
|
|
if path in seen:
|
|
continue
|
|
seen.add(path)
|
|
yield path
|
|
|
|
|
|
def main(patterns: list[str]):
|
|
exit_status = 0
|
|
for file in map(pathlib.Path, iter_files(patterns)):
|
|
if file.is_dir():
|
|
continue
|
|
|
|
try:
|
|
contents = file.read_text(encoding="utf-8")
|
|
except UnicodeDecodeError:
|
|
continue
|
|
|
|
try:
|
|
tree = ast.parse(contents)
|
|
except SyntaxError:
|
|
continue
|
|
|
|
cls_name = None
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, ast.ClassDef):
|
|
cls_name = node.name
|
|
|
|
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
continue
|
|
|
|
name = node.name
|
|
if not name.startswith("test"):
|
|
continue
|
|
|
|
if node.decorator_list:
|
|
continue
|
|
|
|
func_code = ast.unparse(node.body)
|
|
if func_code in (
|
|
f"await super().{name}()",
|
|
f"return await super().{name}()",
|
|
f"return super().{name}()",
|
|
f"super().{name}()",
|
|
):
|
|
exit_status += 1
|
|
|
|
lineno = node.lineno
|
|
msg = f"{file}:{lineno}:{cls_name}.{name} is a test patch that can be safely removed"
|
|
if IS_GH_CI:
|
|
end_lineno = node.end_lineno
|
|
msg = f"::error file={file},line={lineno},endLine={end_lineno},title=Redundant Test Patch::{msg}"
|
|
|
|
print(msg, file=sys.stderr)
|
|
|
|
return exit_status
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = build_argparser()
|
|
args = parser.parse_args()
|
|
exit(main(args.patterns))
|