From 48423646967c0b67e3232ee8a69cd63c214fd82b Mon Sep 17 00:00:00 2001 From: "zhiwei.meng" Date: Thu, 2 Apr 2026 15:20:54 +0800 Subject: [PATCH] Add "--check" option support to trailing_whitespace hook --- pre_commit_hooks/trailing_whitespace_fixer.py | 30 ++++++++++++++----- tests/trailing_whitespace_fixer_test.py | 17 +++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/pre_commit_hooks/trailing_whitespace_fixer.py b/pre_commit_hooks/trailing_whitespace_fixer.py index dab8b14..20a4ced 100644 --- a/pre_commit_hooks/trailing_whitespace_fixer.py +++ b/pre_commit_hooks/trailing_whitespace_fixer.py @@ -9,14 +9,17 @@ def _fix_file( filename: str, is_markdown: bool, chars: bytes | None, + check_only: bool = False, + error_lines: list[int] = None, ) -> bool: with open(filename, mode='rb') as file_processed: lines = file_processed.readlines() - newlines = [_process_line(line, is_markdown, chars) for line in lines] + newlines = [_process_line(line, is_markdown, chars, line_num, error_lines) for line_num, line in enumerate(lines)] if newlines != lines: - with open(filename, mode='wb') as file_processed: - for line in newlines: - file_processed.write(line) + if not check_only: + with open(filename, mode='wb') as file_processed: + for line in newlines: + file_processed.write(line) return True else: return False @@ -26,7 +29,10 @@ def _process_line( line: bytes, is_markdown: bool, chars: bytes | None, + line_num: int, + error_lines: list[int] | None ) -> bytes: + org_line = line if line[-2:] == b'\r\n': eol = b'\r\n' line = line[:-2] @@ -38,11 +44,15 @@ def _process_line( # preserve trailing two-space for non-blank lines in markdown files if is_markdown and (not line.isspace()) and line.endswith(b' '): return line[:-2].rstrip(chars) + b' ' + eol - return line.rstrip(chars) + eol + result = line.rstrip(chars) + eol + if error_lines is not None and org_line != result: + error_lines.append(line_num+1) + return result def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() + parser.add_argument('--check', action='store_true', help='Check without fixing') parser.add_argument( '--no-markdown-linebreak-ext', action='store_true', @@ -93,8 +103,14 @@ def main(argv: Sequence[str] | None = None) -> int: for filename in args.filenames: _, extension = os.path.splitext(filename.lower()) md = all_markdown or extension in md_exts - if _fix_file(filename, md, chars): - print(f'Fixing {filename}') + error_lines = [] + if _fix_file(filename, md, chars, args.check, error_lines): + if args.check: + location = ",".join(map(str, error_lines[:4])) + location += "..." if len(error_lines) > 4 else "" + print(f'Trailing whitespace check failed: {filename} @ {location}') + else: + print(f'Fixing {filename}') return_code = 1 return return_code diff --git a/tests/trailing_whitespace_fixer_test.py b/tests/trailing_whitespace_fixer_test.py index c07497a..fc221e2 100644 --- a/tests/trailing_whitespace_fixer_test.py +++ b/tests/trailing_whitespace_fixer_test.py @@ -18,6 +18,23 @@ def test_fixes_trailing_whitespace(input_s, expected, tmpdir): assert main((str(path),)) == 1 assert path.read() == expected +@pytest.mark.parametrize( + ('input_s', 'exit_code', "lines"), + ( + ('foo \nbar \n', 1, [1,2]), + ('bar\t\nbaz\t\n', 1, [1,2]), + ('bar\nbaz\t\n', 1, [2]), + ), +) +def test_fixes_trailing_whitespace_check_only(capsys, input_s, exit_code, lines, tmpdir): + path = tmpdir.join('file.md') + path.write(input_s) + assert main(('--check', str(path),)) == exit_code + assert path.read() == input_s + captured = capsys.readouterr() + location = "@ " + ','.join(map(str, lines)) + assert location in captured.out + def test_ok_no_newline_end_of_file(tmpdir): filename = tmpdir.join('f')