Mercurial > public > mercurial-scm > hg-stable
comparison contrib/check-code.py @ 11604:c5d40818b270
check-code: add --blame switch
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Fri, 16 Jul 2010 13:26:39 -0500 |
parents | ba2520dd1e29 |
children | dad185761392 |
comparison
equal
deleted
inserted
replaced
11603:2eab5025f804 | 11604:c5d40818b270 |
---|---|
5 # Copyright 2010 Matt Mackall <mpm@selenic.com> | 5 # Copyright 2010 Matt Mackall <mpm@selenic.com> |
6 # | 6 # |
7 # This software may be used and distributed according to the terms of the | 7 # This software may be used and distributed according to the terms of the |
8 # GNU General Public License version 2 or any later version. | 8 # GNU General Public License version 2 or any later version. |
9 | 9 |
10 import re, glob | 10 import re, glob, os |
11 import optparse | 11 import optparse |
12 | 12 |
13 def repquote(m): | 13 def repquote(m): |
14 t = re.sub(r"\w", "x", m.group('text')) | 14 t = re.sub(r"\w", "x", m.group('text')) |
15 t = re.sub(r"[^\sx]", "o", t) | 15 t = re.sub(r"[^\sx]", "o", t) |
158 | 158 |
159 class norepeatlogger(object): | 159 class norepeatlogger(object): |
160 def __init__(self): | 160 def __init__(self): |
161 self._lastseen = None | 161 self._lastseen = None |
162 | 162 |
163 def log(self, fname, lineno, line, msg): | 163 def log(self, fname, lineno, line, msg, blame): |
164 """print error related a to given line of a given file. | 164 """print error related a to given line of a given file. |
165 | 165 |
166 The faulty line will also be printed but only once in the case | 166 The faulty line will also be printed but only once in the case |
167 of multiple errors. | 167 of multiple errors. |
168 | 168 |
171 :line: actual content of the line | 171 :line: actual content of the line |
172 :msg: error message | 172 :msg: error message |
173 """ | 173 """ |
174 msgid = fname, lineno, line | 174 msgid = fname, lineno, line |
175 if msgid != self._lastseen: | 175 if msgid != self._lastseen: |
176 print "%s:%d:" % (fname, lineno) | 176 if blame: |
177 print "%s:%d (%s):" % (fname, lineno, blame) | |
178 else: | |
179 print "%s:%d:" % (fname, lineno) | |
177 print " > %s" % line | 180 print " > %s" % line |
178 self._lastseen = msgid | 181 self._lastseen = msgid |
179 print " " + msg | 182 print " " + msg |
180 | 183 |
181 _defaultlogger = norepeatlogger() | 184 _defaultlogger = norepeatlogger() |
182 | 185 |
183 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False): | 186 def getblame(f): |
187 lines = [] | |
188 for l in os.popen('hg annotate -un %s' % f): | |
189 start, line = l.split(':', 1) | |
190 user, rev = start.split() | |
191 lines.append((line[1:-1], user, rev)) | |
192 return lines | |
193 | |
194 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False, | |
195 blame=False): | |
184 """checks style and portability of a given file | 196 """checks style and portability of a given file |
185 | 197 |
186 :f: filepath | 198 :f: filepath |
187 :logfunc: function used to report error | 199 :logfunc: function used to report error |
188 logfunc(filename, linenumber, linecontent, errormessage) | 200 logfunc(filename, linenumber, linecontent, errormessage) |
189 :maxerr: number of error to display before arborting. | 201 :maxerr: number of error to display before arborting. |
190 Set to None (default) to report all errors | 202 Set to None (default) to report all errors |
191 | 203 |
192 return True if no error is found, False otherwise. | 204 return True if no error is found, False otherwise. |
193 """ | 205 """ |
206 blamecache = None | |
194 result = True | 207 result = True |
195 for name, match, filters, pats in checks: | 208 for name, match, filters, pats in checks: |
196 fc = 0 | 209 fc = 0 |
197 if not re.match(match, f): | 210 if not re.match(match, f): |
198 continue | 211 continue |
208 continue | 221 continue |
209 for p, msg in pats: | 222 for p, msg in pats: |
210 if not warnings and msg.startswith("warning"): | 223 if not warnings and msg.startswith("warning"): |
211 continue | 224 continue |
212 if re.search(p, l[1]): | 225 if re.search(p, l[1]): |
213 logfunc(f, n + 1, l[0], msg) | 226 bd = "" |
227 if blame: | |
228 bd = 'working directory' | |
229 if not blamecache: | |
230 blamecache = getblame(f) | |
231 if n < len(blamecache): | |
232 bl, bu, br = blamecache[n] | |
233 if bl == l[0]: | |
234 bd = '%s@%s' % (bu, br) | |
235 logfunc(f, n + 1, l[0], msg, bd) | |
214 fc += 1 | 236 fc += 1 |
215 result = False | 237 result = False |
216 if maxerr is not None and fc >= maxerr: | 238 if maxerr is not None and fc >= maxerr: |
217 print " (too many errors, giving up)" | 239 print " (too many errors, giving up)" |
218 break | 240 break |
219 break | 241 break |
220 return result | 242 return result |
221 | 243 |
222 | |
223 if __name__ == "__main__": | 244 if __name__ == "__main__": |
224 parser = optparse.OptionParser("%prog [options] [files]") | 245 parser = optparse.OptionParser("%prog [options] [files]") |
225 parser.add_option("-w", "--warnings", action="store_true", | 246 parser.add_option("-w", "--warnings", action="store_true", |
226 help="include warning-level checks") | 247 help="include warning-level checks") |
227 parser.add_option("-p", "--per-file", type="int", | 248 parser.add_option("-p", "--per-file", type="int", |
228 help="max warnings per file") | 249 help="max warnings per file") |
229 | 250 parser.add_option("-b", "--blame", action="store_true", |
230 parser.set_defaults(per_file=15, warnings=False) | 251 help="use annotate to generate blame info") |
252 | |
253 parser.set_defaults(per_file=15, warnings=False, blame=False) | |
231 (options, args) = parser.parse_args() | 254 (options, args) = parser.parse_args() |
232 | 255 |
233 if len(args) == 0: | 256 if len(args) == 0: |
234 check = glob.glob("*") | 257 check = glob.glob("*") |
235 else: | 258 else: |
236 check = args | 259 check = args |
237 | 260 |
238 for f in check: | 261 for f in check: |
239 checkfile(f, maxerr=options.per_file, warnings=options.warnings) | 262 checkfile(f, maxerr=options.per_file, warnings=options.warnings, |
263 blame=options.blame) |