11#!/usr/bin/env python3
22"""Check proposed changes for common issues."""
3- import re
43import sys
5- import shutil
64import os .path
75import subprocess
86import sysconfig
97
10- import reindent
11- import untabify
12-
13-
14- # Excluded directories which are copies of external libraries:
15- # don't check their coding style
16- EXCLUDE_DIRS = [
17- os .path .join ('Modules' , '_decimal' , 'libmpdec' ),
18- os .path .join ('Modules' , 'expat' ),
19- os .path .join ('Modules' , 'zlib' ),
20- ]
218SRCDIR = sysconfig .get_config_var ('srcdir' )
229
2310
@@ -147,85 +134,11 @@ def changed_files(base_branch=None):
147134 else :
148135 sys .exit ('need a git checkout to get modified files' )
149136
150- filenames2 = []
151- for filename in filenames :
152- # Normalize the path to be able to match using .startswith()
153- filename = os .path .normpath (filename )
154- if any (filename .startswith (path ) for path in EXCLUDE_DIRS ):
155- # Exclude the file
156- continue
157- filenames2 .append (filename )
158-
137+ # Normalize the path to be able to match using .startswith()
138+ filenames2 = list (map (os .path .normpath , filenames ))
159139 return filenames2
160140
161141
162- def report_modified_files (file_paths ):
163- count = len (file_paths )
164- if count == 0 :
165- return n_files_str (count )
166- else :
167- lines = ["{}:" .format (n_files_str (count ))]
168- for path in file_paths :
169- lines .append (" {}" .format (path ))
170- return "\n " .join (lines )
171-
172-
173- #: Python files that have tabs by design:
174- _PYTHON_FILES_WITH_TABS = frozenset ({
175- 'Tools/c-analyzer/cpython/_parser.py' ,
176- })
177-
178-
179- @status ("Fixing Python file whitespace" , info = report_modified_files )
180- def normalize_whitespace (file_paths ):
181- """Make sure that the whitespace for .py files have been normalized."""
182- reindent .makebackup = False # No need to create backups.
183- fixed = [
184- path for path in file_paths
185- if (
186- path .endswith ('.py' )
187- and path not in _PYTHON_FILES_WITH_TABS
188- and reindent .check (os .path .join (SRCDIR , path ))
189- )
190- ]
191- return fixed
192-
193-
194- @status ("Fixing C file whitespace" , info = report_modified_files )
195- def normalize_c_whitespace (file_paths ):
196- """Report if any C files """
197- fixed = []
198- for path in file_paths :
199- abspath = os .path .join (SRCDIR , path )
200- with open (abspath , 'r' ) as f :
201- if '\t ' not in f .read ():
202- continue
203- untabify .process (abspath , 8 , verbose = False )
204- fixed .append (path )
205- return fixed
206-
207-
208- ws_re = re .compile (br'\s+(\r?\n)$' )
209-
210- @status ("Fixing docs whitespace" , info = report_modified_files )
211- def normalize_docs_whitespace (file_paths ):
212- fixed = []
213- for path in file_paths :
214- abspath = os .path .join (SRCDIR , path )
215- try :
216- with open (abspath , 'rb' ) as f :
217- lines = f .readlines ()
218- new_lines = [ws_re .sub (br'\1' , line ) for line in lines ]
219- if new_lines != lines :
220- shutil .copyfile (abspath , abspath + '.bak' )
221- with open (abspath , 'wb' ) as f :
222- f .writelines (new_lines )
223- fixed .append (path )
224- except Exception as err :
225- print ('Cannot fix %s: %s' % (path , err ))
226- return fixed
227-
228-
229142@status ("Docs modified" , modal = True )
230143def docs_modified (file_paths ):
231144 """Report if any file in the Doc directory has been changed."""
@@ -244,6 +157,7 @@ def reported_news(file_paths):
244157 return any (p .startswith (os .path .join ('Misc' , 'NEWS.d' , 'next' ))
245158 for p in file_paths )
246159
160+
247161@status ("configure regenerated" , modal = True , info = str )
248162def regenerated_configure (file_paths ):
249163 """Check if configure has been regenerated."""
@@ -252,6 +166,7 @@ def regenerated_configure(file_paths):
252166 else :
253167 return "not needed"
254168
169+
255170@status ("pyconfig.h.in regenerated" , modal = True , info = str )
256171def regenerated_pyconfig_h_in (file_paths ):
257172 """Check if pyconfig.h.in has been regenerated."""
@@ -260,43 +175,15 @@ def regenerated_pyconfig_h_in(file_paths):
260175 else :
261176 return "not needed"
262177
263- def ci (pull_request ):
264- if pull_request == 'false' :
265- print ('Not a pull request; skipping' )
266- return
267- base_branch = get_base_branch ()
268- file_paths = changed_files (base_branch )
269- python_files = [fn for fn in file_paths if fn .endswith ('.py' )]
270- c_files = [fn for fn in file_paths if fn .endswith (('.c' , '.h' ))]
271- doc_files = [fn for fn in file_paths if fn .startswith ('Doc' ) and
272- fn .endswith (('.rst' , '.inc' ))]
273- fixed = []
274- fixed .extend (normalize_whitespace (python_files ))
275- fixed .extend (normalize_c_whitespace (c_files ))
276- fixed .extend (normalize_docs_whitespace (doc_files ))
277- if not fixed :
278- print ('No whitespace issues found' )
279- else :
280- print (f'Please fix the { len (fixed )} file(s) with whitespace issues' )
281- print ('(on UNIX you can run `make patchcheck` to make the fixes)' )
282- sys .exit (1 )
283178
284179def main ():
285180 base_branch = get_base_branch ()
286181 file_paths = changed_files (base_branch )
287- python_files = [fn for fn in file_paths if fn .endswith ('.py' )]
288- c_files = [fn for fn in file_paths if fn .endswith (('.c' , '.h' ))]
289- doc_files = [fn for fn in file_paths if fn .startswith ('Doc' ) and
290- fn .endswith (('.rst' , '.inc' ))]
182+ has_doc_files = any (fn for fn in file_paths if fn .startswith ('Doc' ) and
183+ fn .endswith (('.rst' , '.inc' )))
291184 misc_files = {p for p in file_paths if p .startswith ('Misc' )}
292- # PEP 8 whitespace rules enforcement.
293- normalize_whitespace (python_files )
294- # C rules enforcement.
295- normalize_c_whitespace (c_files )
296- # Doc whitespace enforcement.
297- normalize_docs_whitespace (doc_files )
298185 # Docs updated.
299- docs_modified (doc_files )
186+ docs_modified (has_doc_files )
300187 # Misc/ACKS changed.
301188 credit_given (misc_files )
302189 # Misc/NEWS changed.
@@ -307,19 +194,14 @@ def main():
307194 regenerated_pyconfig_h_in (file_paths )
308195
309196 # Test suite run and passed.
310- if python_files or c_files :
311- end = " and check for refleaks?" if c_files else "?"
312- print ()
313- print ("Did you run the test suite" + end )
197+ has_c_files = any (fn for fn in file_paths if fn .endswith (('.c' , '.h' )))
198+ has_python_files = any (fn for fn in file_paths if fn .endswith ('.py' ))
199+ print ()
200+ if has_c_files :
201+ print ("Did you run the test suite and check for refleaks?" )
202+ elif has_python_files :
203+ print ("Did you run the test suite?" )
314204
315205
316206if __name__ == '__main__' :
317- import argparse
318- parser = argparse .ArgumentParser (description = __doc__ )
319- parser .add_argument ('--ci' ,
320- help = 'Perform pass/fail checks' )
321- args = parser .parse_args ()
322- if args .ci :
323- ci (args .ci )
324- else :
325- main ()
207+ main ()
0 commit comments