summaryrefslogtreecommitdiffstats
path: root/Tools/patchcheck
diff options
context:
space:
mode:
authorHugo van Kemenade <hugovk@users.noreply.github.com>2023-10-11 16:12:28 (GMT)
committerGitHub <noreply@github.com>2023-10-11 16:12:28 (GMT)
commitde956b263b98bb9928ce4377c42ca8271c4f2682 (patch)
treef468b140d136378a17b14aaec8b98be6797efd16 /Tools/patchcheck
parent467abfe1d4de8ea3fb576759259574730528a398 (diff)
downloadcpython-de956b263b98bb9928ce4377c42ca8271c4f2682.zip
cpython-de956b263b98bb9928ce4377c42ca8271c4f2682.tar.gz
cpython-de956b263b98bb9928ce4377c42ca8271c4f2682.tar.bz2
gh-109408: Revert pre-commit whitespace checks pending portable solution (#110726)
Diffstat (limited to 'Tools/patchcheck')
-rwxr-xr-xTools/patchcheck/patchcheck.py120
1 files changed, 110 insertions, 10 deletions
diff --git a/Tools/patchcheck/patchcheck.py b/Tools/patchcheck/patchcheck.py
index 66328c8..af1f058 100755
--- a/Tools/patchcheck/patchcheck.py
+++ b/Tools/patchcheck/patchcheck.py
@@ -1,15 +1,30 @@
#!/usr/bin/env python3
"""Check proposed changes for common issues."""
+import re
import sys
+import shutil
import os.path
import subprocess
import sysconfig
+import reindent
+import untabify
+
+
def get_python_source_dir():
src_dir = sysconfig.get_config_var('abs_srcdir')
if not src_dir:
src_dir = sysconfig.get_config_var('srcdir')
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()
@@ -140,8 +155,62 @@ def changed_files(base_branch=None):
else:
sys.exit('need a git checkout to get modified files')
- # Normalize the path to be able to match using str.startswith()
- return list(map(os.path.normpath, filenames))
+ filenames2 = []
+ for filename in 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 Python file whitespace", info=report_modified_files)
+def normalize_whitespace(file_paths):
+ """Make sure that the whitespace for .py files have been normalized."""
+ reindent.makebackup = False # No need to create backups.
+ fixed = [
+ path for path in file_paths
+ if (
+ path.endswith('.py')
+ and path not in _PYTHON_FILES_WITH_TABS
+ and reindent.check(os.path.join(SRCDIR, path))
+ )
+ ]
+ return fixed
+
+
+@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)
@@ -181,12 +250,38 @@ def regenerated_pyconfig_h_in(file_paths):
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)
+ python_files = [fn for fn in file_paths if fn.endswith('.py')]
+ c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))]
+ fixed = []
+ fixed.extend(normalize_whitespace(python_files))
+ 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():
base_branch = get_base_branch()
file_paths = changed_files(base_branch)
+ python_files = [fn for fn in file_paths if fn.endswith('.py')]
+ 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
fn.endswith(('.rst', '.inc'))]
misc_files = {p for p in file_paths if p.startswith('Misc')}
+ # PEP 8 whitespace rules enforcement.
+ normalize_whitespace(python_files)
+ # C rules enforcement.
+ normalize_c_whitespace(c_files)
# Docs updated.
docs_modified(doc_files)
# Misc/ACKS changed.
@@ -199,14 +294,19 @@ def main():
regenerated_pyconfig_h_in(file_paths)
# Test suite run and passed.
- has_c_files = any(fn for fn in file_paths if fn.endswith(('.c', '.h')))
- has_python_files = any(fn for fn in file_paths if fn.endswith('.py'))
- print()
- if has_c_files:
- print("Did you run the test suite and check for refleaks?")
- elif has_python_files:
- print("Did you run the test suite?")
+ if python_files or c_files:
+ end = " and check for refleaks?" if c_files else "?"
+ print()
+ print("Did you run the test suite" + end)
if __name__ == '__main__':
- 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()