GH-109408: Move the C file whitespace check from patchcheck to pre-commit (#109890)

Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
This commit is contained in:
Adam Turner 2023-10-10 14:40:08 +01:00 committed by GitHub
parent e24f9ae703
commit f5edb56328
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 23 additions and 90 deletions

View File

@ -32,6 +32,21 @@ repos:
types: [python] types: [python]
exclude: '^(Lib/test/tokenizedata/|Tools/c-analyzer/cpython/_parser).*$' exclude: '^(Lib/test/tokenizedata/|Tools/c-analyzer/cpython/_parser).*$'
- repo: local
hooks:
- id: c-file-whitespace
name: "Check C file whitespace"
entry: "python Tools/patchcheck/untabify.py"
language: "system"
types_or: ['c', 'c++']
# Don't check the style of vendored libraries
exclude: |
(?x)^(
Modules/_decimal/.*
| Modules/libmpdec/.*
| Modules/expat/.*
)$
- repo: https://github.com/sphinx-contrib/sphinx-lint - repo: https://github.com/sphinx-contrib/sphinx-lint
rev: v0.6.8 rev: v0.6.8
hooks: hooks:

View File

@ -1,29 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Check proposed changes for common issues.""" """Check proposed changes for common issues."""
import re
import sys import sys
import shutil
import os.path import os.path
import subprocess import subprocess
import sysconfig import sysconfig
import untabify
def get_python_source_dir(): def get_python_source_dir():
src_dir = sysconfig.get_config_var('abs_srcdir') src_dir = sysconfig.get_config_var('abs_srcdir')
if not src_dir: if not src_dir:
src_dir = sysconfig.get_config_var('srcdir') src_dir = sysconfig.get_config_var('srcdir')
return os.path.abspath(src_dir) return os.path.abspath(src_dir)
# Excluded directories which are copies of external libraries:
# don't check their coding style
EXCLUDE_DIRS = [
os.path.join('Modules', '_decimal', 'libmpdec'),
os.path.join('Modules', 'expat'),
os.path.join('Modules', 'zlib'),
]
SRCDIR = get_python_source_dir() SRCDIR = get_python_source_dir()
@ -154,47 +140,8 @@ def changed_files(base_branch=None):
else: else:
sys.exit('need a git checkout to get modified files') sys.exit('need a git checkout to get modified files')
filenames2 = [] # Normalize the path to be able to match using str.startswith()
for filename in filenames: return list(map(os.path.normpath, filenames))
# Normalize the path to be able to match using .startswith()
filename = os.path.normpath(filename)
if any(filename.startswith(path) for path in EXCLUDE_DIRS):
# Exclude the file
continue
filenames2.append(filename)
return filenames2
def report_modified_files(file_paths):
count = len(file_paths)
if count == 0:
return n_files_str(count)
else:
lines = [f"{n_files_str(count)}:"]
for path in file_paths:
lines.append(f" {path}")
return "\n".join(lines)
#: Python files that have tabs by design:
_PYTHON_FILES_WITH_TABS = frozenset({
'Tools/c-analyzer/cpython/_parser.py',
})
@status("Fixing C file whitespace", info=report_modified_files)
def normalize_c_whitespace(file_paths):
"""Report if any C files """
fixed = []
for path in file_paths:
abspath = os.path.join(SRCDIR, path)
with open(abspath, 'r') as f:
if '\t' not in f.read():
continue
untabify.process(abspath, 8, verbose=False)
fixed.append(path)
return fixed
@status("Docs modified", modal=True) @status("Docs modified", modal=True)
@ -234,33 +181,12 @@ def regenerated_pyconfig_h_in(file_paths):
return "not needed" return "not needed"
def ci(pull_request):
if pull_request == 'false':
print('Not a pull request; skipping')
return
base_branch = get_base_branch()
file_paths = changed_files(base_branch)
c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))]
fixed = []
fixed.extend(normalize_c_whitespace(c_files))
if not fixed:
print('No whitespace issues found')
else:
count = len(fixed)
print(f'Please fix the {n_files_str(count)} with whitespace issues')
print('(on Unix you can run `make patchcheck` to make the fixes)')
sys.exit(1)
def main(): def main():
base_branch = get_base_branch() base_branch = get_base_branch()
file_paths = changed_files(base_branch) file_paths = changed_files(base_branch)
c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))]
doc_files = [fn for fn in file_paths if fn.startswith('Doc') and doc_files = [fn for fn in file_paths if fn.startswith('Doc') and
fn.endswith(('.rst', '.inc'))] fn.endswith(('.rst', '.inc'))]
misc_files = {p for p in file_paths if p.startswith('Misc')} misc_files = {p for p in file_paths if p.startswith('Misc')}
# C rules enforcement.
normalize_c_whitespace(c_files)
# Docs updated. # Docs updated.
docs_modified(doc_files) docs_modified(doc_files)
# Misc/ACKS changed. # Misc/ACKS changed.
@ -283,12 +209,4 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--ci',
help='Perform pass/fail checks')
args = parser.parse_args()
if args.ci:
ci(args.ci)
else:
main() main()

View File

@ -21,8 +21,7 @@ def main():
if optname == '-t': if optname == '-t':
tabsize = int(optvalue) tabsize = int(optvalue)
for filename in args: return max(process(filename, tabsize) for filename in args)
process(filename, tabsize)
def process(filename, tabsize, verbose=True): def process(filename, tabsize, verbose=True):
@ -32,10 +31,10 @@ def process(filename, tabsize, verbose=True):
encoding = f.encoding encoding = f.encoding
except IOError as msg: except IOError as msg:
print("%r: I/O error: %s" % (filename, msg)) print("%r: I/O error: %s" % (filename, msg))
return return 2
newtext = text.expandtabs(tabsize) newtext = text.expandtabs(tabsize)
if newtext == text: if newtext == text:
return return 0
backup = filename + "~" backup = filename + "~"
try: try:
os.unlink(backup) os.unlink(backup)
@ -49,7 +48,8 @@ def process(filename, tabsize, verbose=True):
f.write(newtext) f.write(newtext)
if verbose: if verbose:
print(filename) print(filename)
return 1
if __name__ == '__main__': if __name__ == '__main__':
main() raise SystemExit(main())