Files
RustPython/scripts/check_redundant_patches.py
Mathieu Dupuy a463449e35 csv: support non-ASCII Unicode dialect characters (#9367)
* 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
2026-10-28 01:23:02 +09:00

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))