Skip to content

Commit fd8ddd4

Browse files
committed
[3.11] pythonGH-109408: Move the C file whitespace check from patchcheck to pre-commit (pythonGH-109890)
Co-authored-by: Hugo van Kemenade <[email protected]>. (cherry picked from commit f5edb56) Co-authored-by: Adam Turner <[email protected]>
1 parent 3788c48 commit fd8ddd4

File tree

3 files changed

+41
-103
lines changed

3 files changed

+41
-103
lines changed

.pre-commit-config.yaml

+26
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,32 @@ repos:
1616
- id: trailing-whitespace
1717
types_or: [c, inc, python, rst]
1818

19+
- repo: local
20+
hooks:
21+
- id: python-file-whitespace
22+
name: "Check Python file whitespace"
23+
entry: 'python Tools/patchcheck/reindent.py --nobackup --newline LF'
24+
language: 'system'
25+
types: [python]
26+
exclude: '^(Lib/test/tokenizedata/|Tools/c-analyzer/cpython/_parser).*$'
27+
28+
- repo: local
29+
hooks:
30+
- id: c-file-whitespace
31+
name: "Check C file whitespace"
32+
entry: "python Tools/patchcheck/untabify.py"
33+
language: "system"
34+
types_or: ['c', 'c++']
35+
# Don't check the style of vendored libraries
36+
exclude: |
37+
(?x)^(
38+
Modules/_ctypes/libffi_osx/.*
39+
| Modules/_ctypes/libffi_msvc/.*
40+
| Modules/_decimal/.*
41+
| Modules/libmpdec/.*
42+
| Modules/expat/.*
43+
)$
44+
1945
- repo: https://github.com/sphinx-contrib/sphinx-lint
2046
rev: v0.6.8
2147
hooks:

Tools/scripts/patchcheck.py

+10-98
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,15 @@
11
#!/usr/bin/env python3
22
"""Check proposed changes for common issues."""
3-
import re
43
import sys
5-
import shutil
64
import os.path
75
import subprocess
86
import sysconfig
97

10-
import reindent
11-
import untabify
12-
13-
148
def get_python_source_dir():
159
src_dir = sysconfig.get_config_var('abs_srcdir')
1610
if not src_dir:
1711
src_dir = sysconfig.get_config_var('srcdir')
1812
return os.path.abspath(src_dir)
19-
20-
21-
# Excluded directories which are copies of external libraries:
22-
# don't check their coding style
23-
EXCLUDE_DIRS = [os.path.join('Modules', '_ctypes', 'libffi_osx'),
24-
os.path.join('Modules', '_ctypes', 'libffi_msvc'),
25-
os.path.join('Modules', '_decimal', 'libmpdec'),
26-
os.path.join('Modules', 'expat'),
27-
os.path.join('Modules', 'zlib')]
2813
SRCDIR = get_python_source_dir()
2914

3015

@@ -154,50 +139,8 @@ def changed_files(base_branch=None):
154139
else:
155140
sys.exit('need a git checkout to get modified files')
156141

157-
filenames2 = []
158-
for filename in filenames:
159-
# Normalize the path to be able to match using .startswith()
160-
filename = os.path.normpath(filename)
161-
if any(filename.startswith(path) for path in EXCLUDE_DIRS):
162-
# Exclude the file
163-
continue
164-
filenames2.append(filename)
165-
166-
return filenames2
167-
168-
169-
def report_modified_files(file_paths):
170-
count = len(file_paths)
171-
if count == 0:
172-
return n_files_str(count)
173-
else:
174-
lines = [f"{n_files_str(count)}:"]
175-
for path in file_paths:
176-
lines.append(f" {path}")
177-
return "\n".join(lines)
178-
179-
180-
@status("Fixing Python file whitespace", info=report_modified_files)
181-
def normalize_whitespace(file_paths):
182-
"""Make sure that the whitespace for .py files have been normalized."""
183-
reindent.makebackup = False # No need to create backups.
184-
fixed = [path for path in file_paths if path.endswith('.py') and
185-
reindent.check(os.path.join(SRCDIR, path))]
186-
return fixed
187-
188-
189-
@status("Fixing C file whitespace", info=report_modified_files)
190-
def normalize_c_whitespace(file_paths):
191-
"""Report if any C files """
192-
fixed = []
193-
for path in file_paths:
194-
abspath = os.path.join(SRCDIR, path)
195-
with open(abspath, 'r') as f:
196-
if '\t' not in f.read():
197-
continue
198-
untabify.process(abspath, 8, verbose=False)
199-
fixed.append(path)
200-
return fixed
142+
# Normalize the path to be able to match using str.startswith()
143+
return list(map(os.path.normpath, filenames))
201144

202145

203146
@status("Docs modified", modal=True)
@@ -237,38 +180,12 @@ def regenerated_pyconfig_h_in(file_paths):
237180
return "not needed"
238181

239182

240-
def ci(pull_request):
241-
if pull_request == 'false':
242-
print('Not a pull request; skipping')
243-
return
244-
base_branch = get_base_branch()
245-
file_paths = changed_files(base_branch)
246-
python_files = [fn for fn in file_paths if fn.endswith('.py')]
247-
c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))]
248-
fixed = []
249-
fixed.extend(normalize_whitespace(python_files))
250-
fixed.extend(normalize_c_whitespace(c_files))
251-
if not fixed:
252-
print('No whitespace issues found')
253-
else:
254-
count = len(fixed)
255-
print(f'Please fix the {n_files_str(count)} with whitespace issues')
256-
print('(on Unix you can run `make patchcheck` to make the fixes)')
257-
sys.exit(1)
258-
259-
260183
def main():
261184
base_branch = get_base_branch()
262185
file_paths = changed_files(base_branch)
263-
python_files = [fn for fn in file_paths if fn.endswith('.py')]
264-
c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))]
265186
doc_files = [fn for fn in file_paths if fn.startswith('Doc') and
266187
fn.endswith(('.rst', '.inc'))]
267188
misc_files = {p for p in file_paths if p.startswith('Misc')}
268-
# PEP 8 whitespace rules enforcement.
269-
normalize_whitespace(python_files)
270-
# C rules enforcement.
271-
normalize_c_whitespace(c_files)
272189
# Docs updated.
273190
docs_modified(doc_files)
274191
# Misc/ACKS changed.
@@ -281,19 +198,14 @@ def main():
281198
regenerated_pyconfig_h_in(file_paths)
282199

283200
# Test suite run and passed.
284-
if python_files or c_files:
285-
end = " and check for refleaks?" if c_files else "?"
286-
print()
287-
print("Did you run the test suite" + end)
201+
has_c_files = any(fn for fn in file_paths if fn.endswith(('.c', '.h')))
202+
has_python_files = any(fn for fn in file_paths if fn.endswith('.py'))
203+
print()
204+
if has_c_files:
205+
print("Did you run the test suite and check for refleaks?")
206+
elif has_python_files:
207+
print("Did you run the test suite?")
288208

289209

290210
if __name__ == '__main__':
291-
import argparse
292-
parser = argparse.ArgumentParser(description=__doc__)
293-
parser.add_argument('--ci',
294-
help='Perform pass/fail checks')
295-
args = parser.parse_args()
296-
if args.ci:
297-
ci(args.ci)
298-
else:
299-
main()
211+
main()

Tools/scripts/untabify.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ def main():
2121
if optname == '-t':
2222
tabsize = int(optvalue)
2323

24-
for filename in args:
25-
process(filename, tabsize)
24+
return max(process(filename, tabsize) for filename in args)
2625

2726

2827
def process(filename, tabsize, verbose=True):
@@ -32,10 +31,10 @@ def process(filename, tabsize, verbose=True):
3231
encoding = f.encoding
3332
except IOError as msg:
3433
print("%r: I/O error: %s" % (filename, msg))
35-
return
34+
return 2
3635
newtext = text.expandtabs(tabsize)
3736
if newtext == text:
38-
return
37+
return 0
3938
backup = filename + "~"
4039
try:
4140
os.unlink(backup)
@@ -49,7 +48,8 @@ def process(filename, tabsize, verbose=True):
4948
f.write(newtext)
5049
if verbose:
5150
print(filename)
51+
return 1
5252

5353

5454
if __name__ == '__main__':
55-
main()
55+
raise SystemExit(main())

0 commit comments

Comments
 (0)