##// END OF EJS Templates
check-code: more replacement characters...
Simon Heimberg -
r19999:169cb9e4 stable
parent child Browse files
Show More
@@ -1,529 +1,546
1 1 #!/usr/bin/env python
2 2 #
3 3 # check-code - a style and portability checker for Mercurial
4 4 #
5 5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 import re, glob, os, sys
11 11 import keyword
12 12 import optparse
13 13 try:
14 14 import re2
15 15 except ImportError:
16 16 re2 = None
17 17
18 18 def compilere(pat, multiline=False):
19 19 if multiline:
20 20 pat = '(?m)' + pat
21 21 if re2:
22 22 try:
23 23 return re2.compile(pat)
24 24 except re2.error:
25 25 pass
26 26 return re.compile(pat)
27 27
28 28 def repquote(m):
29 t = re.sub(r"\w", "x", m.group('text'))
30 t = re.sub(r"[^\s\nx]", "o", t)
29 fromc = '.:'
30 tochr = 'pq'
31 def encodechr(i):
32 if i > 255:
33 return 'u'
34 c = chr(i)
35 if c in ' \n':
36 return c
37 if c.isalpha():
38 return 'x'
39 if c.isdigit():
40 return 'n'
41 try:
42 return tochr[fromc.find(c)]
43 except (ValueError, IndexError):
44 return 'o'
45 t = m.group('text')
46 tt = ''.join(encodechr(i) for i in xrange(256))
47 t = t.translate(tt)
31 48 return m.group('quote') + t + m.group('quote')
32 49
33 50 def reppython(m):
34 51 comment = m.group('comment')
35 52 if comment:
36 53 l = len(comment.rstrip())
37 54 return "#" * l + comment[l:]
38 55 return repquote(m)
39 56
40 57 def repcomment(m):
41 58 return m.group(1) + "#" * len(m.group(2))
42 59
43 60 def repccomment(m):
44 61 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
45 62 return m.group(1) + t + "*/"
46 63
47 64 def repcallspaces(m):
48 65 t = re.sub(r"\n\s+", "\n", m.group(2))
49 66 return m.group(1) + t
50 67
51 68 def repinclude(m):
52 69 return m.group(1) + "<foo>"
53 70
54 71 def rephere(m):
55 72 t = re.sub(r"\S", "x", m.group(2))
56 73 return m.group(1) + t
57 74
58 75
59 76 testpats = [
60 77 [
61 78 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
62 79 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
63 80 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
64 81 (r'(?<!hg )grep.*-a', "don't use 'grep -a', use in-line python"),
65 82 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
66 83 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
67 84 (r'echo -n', "don't use 'echo -n', use printf"),
68 85 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
69 86 (r'head -c', "don't use 'head -c', use 'dd'"),
70 87 (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"),
71 88 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
72 89 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
73 90 (r'printf.*[^\\]\\([1-9]|0\d)', "don't use 'printf \NNN', use Python"),
74 91 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
75 92 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
76 93 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
77 94 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
78 95 "use egrep for extended grep syntax"),
79 96 (r'/bin/', "don't use explicit paths for tools"),
80 97 (r'[^\n]\Z', "no trailing newline"),
81 98 (r'export.*=', "don't export and assign at once"),
82 99 (r'^source\b', "don't use 'source', use '.'"),
83 100 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
84 101 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
85 102 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
86 103 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
87 104 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
88 105 (r'^alias\b.*=', "don't use alias, use a function"),
89 106 (r'if\s*!', "don't use '!' to negate exit status"),
90 107 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
91 108 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
92 109 (r'^( *)\t', "don't use tabs to indent"),
93 110 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
94 111 "put a backslash-escaped newline after sed 'i' command"),
95 112 ],
96 113 # warnings
97 114 [
98 115 (r'^function', "don't use 'function', use old style"),
99 116 (r'^diff.*-\w*N', "don't use 'diff -N'"),
100 117 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
101 118 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
102 119 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
103 120 ]
104 121 ]
105 122
106 123 testfilters = [
107 124 (r"( *)(#([^\n]*\S)?)", repcomment),
108 125 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
109 126 ]
110 127
111 128 winglobmsg = "use (glob) to match Windows paths too"
112 129 uprefix = r"^ \$ "
113 130 utestpats = [
114 131 [
115 132 (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
116 133 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
117 134 "use regex test output patterns instead of sed"),
118 135 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
119 136 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
120 137 (uprefix + r'.*\|\| echo.*(fail|error)',
121 138 "explicit exit code checks unnecessary"),
122 139 (uprefix + r'set -e', "don't use set -e"),
123 140 (uprefix + r'(\s|fi\b|done\b)', "use > for continued lines"),
124 141 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
125 142 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
126 143 winglobmsg),
127 144 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
128 145 (r'^ reverting .*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
129 146 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
130 147 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
131 148 (r'^ pushing subrepo \S+/\S+ to.*[^)]$', winglobmsg,
132 149 '\$TESTTMP/unix-repo$'),
133 150 (r'^ moving \S+/.*[^)]$', winglobmsg),
134 151 (r'^ no changes made to subrepo since.*/.*[^)]$',
135 152 winglobmsg, '\$TESTTMP/unix-repo$'),
136 153 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$',
137 154 winglobmsg, '\$TESTTMP/unix-repo$'),
138 155 ],
139 156 # warnings
140 157 [
141 158 (r'^ [^*?/\n]* \(glob\)$',
142 159 "glob match with no glob character (?*/)"),
143 160 ]
144 161 ]
145 162
146 163 for i in [0, 1]:
147 164 for p, m in testpats[i]:
148 165 if p.startswith(r'^'):
149 166 p = r"^ [$>] (%s)" % p[1:]
150 167 else:
151 168 p = r"^ [$>] .*(%s)" % p
152 169 utestpats[i].append((p, m))
153 170
154 171 utestfilters = [
155 172 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
156 173 (r"( *)(#([^\n]*\S)?)", repcomment),
157 174 ]
158 175
159 176 pypats = [
160 177 [
161 178 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
162 179 "tuple parameter unpacking not available in Python 3+"),
163 180 (r'lambda\s*\(.*,.*\)',
164 181 "tuple parameter unpacking not available in Python 3+"),
165 182 (r'import (.+,[^.]+\.[^.]+|[^.]+\.[^.]+,)',
166 183 '2to3 can\'t always rewrite "import qux, foo.bar", '
167 184 'use "import foo.bar" on its own line instead.'),
168 185 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
169 186 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
170 187 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
171 188 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
172 189 (r'^\s*\t', "don't use tabs"),
173 190 (r'\S;\s*\n', "semicolon"),
174 191 (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"),
175 192 (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"),
176 193 (r'(\w|\)),\w', "missing whitespace after ,"),
177 194 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
178 195 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
179 196 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
180 197 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
181 198 (r'(?<!def)(\s+|^|\()next\(.+\)',
182 199 'no next(foo) in Python 2.4 and 2.5, use foo.next() instead'),
183 200 (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
184 201 r'((?:\n|\1\s.*\n)+?)\1finally:',
185 202 'no yield inside try/finally in Python 2.4'),
186 203 (r'.{81}', "line too long"),
187 204 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
188 205 (r'[^\n]\Z', "no trailing newline"),
189 206 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
190 207 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
191 208 # "don't use underbars in identifiers"),
192 209 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
193 210 "don't use camelcase in identifiers"),
194 211 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
195 212 "linebreak after :"),
196 213 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
197 214 (r'class\s[^( \n]+\(\):',
198 215 "class foo() not available in Python 2.4, use class foo(object)"),
199 216 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
200 217 "Python keyword is not a function"),
201 218 (r',]', "unneeded trailing ',' in list"),
202 219 # (r'class\s[A-Z][^\(]*\((?!Exception)',
203 220 # "don't capitalize non-exception classes"),
204 221 # (r'in range\(', "use xrange"),
205 222 # (r'^\s*print\s+', "avoid using print in core and extensions"),
206 223 (r'[\x80-\xff]', "non-ASCII character literal"),
207 224 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
208 225 (r'^\s*with\s+', "with not available in Python 2.4"),
209 226 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
210 227 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
211 228 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
212 229 (r'(?<!def)\s+(any|all|format)\(',
213 230 "any/all/format not available in Python 2.4"),
214 231 (r'(?<!def)\s+(callable)\(',
215 232 "callable not available in Python 3, use getattr(f, '__call__', None)"),
216 233 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
217 234 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
218 235 "gratuitous whitespace after Python keyword"),
219 236 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
220 237 # (r'\s\s=', "gratuitous whitespace before ="),
221 238 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
222 239 "missing whitespace around operator"),
223 240 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
224 241 "missing whitespace around operator"),
225 242 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
226 243 "missing whitespace around operator"),
227 244 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
228 245 "wrong whitespace around ="),
229 246 (r'\([^()]*( =[^=]|[^<>!=]= )',
230 247 "no whitespace around = for named parameters"),
231 248 (r'raise Exception', "don't raise generic exceptions"),
232 249 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
233 250 "don't use old-style two-argument raise, use Exception(message)"),
234 251 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
235 252 (r' [=!]=\s+(True|False|None)',
236 253 "comparison with singleton, use 'is' or 'is not' instead"),
237 254 (r'^\s*(while|if) [01]:',
238 255 "use True/False for constant Boolean expression"),
239 256 (r'(?:(?<!def)\s+|\()hasattr',
240 257 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
241 258 (r'opener\([^)]*\).read\(',
242 259 "use opener.read() instead"),
243 260 (r'BaseException', 'not in Python 2.4, use Exception'),
244 261 (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
245 262 (r'opener\([^)]*\).write\(',
246 263 "use opener.write() instead"),
247 264 (r'[\s\(](open|file)\([^)]*\)\.read\(',
248 265 "use util.readfile() instead"),
249 266 (r'[\s\(](open|file)\([^)]*\)\.write\(',
250 267 "use util.readfile() instead"),
251 268 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
252 269 "always assign an opened file to a variable, and close it afterwards"),
253 270 (r'[\s\(](open|file)\([^)]*\)\.',
254 271 "always assign an opened file to a variable, and close it afterwards"),
255 272 (r'(?i)descendent', "the proper spelling is descendAnt"),
256 273 (r'\.debug\(\_', "don't mark debug messages for translation"),
257 274 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
258 275 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
259 276 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
260 277 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
261 278 "missing _() in ui message (use () to hide false-positives)"),
262 279 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
263 280 ],
264 281 # warnings
265 282 [
266 (r'(^| )oo +xxxxoo[ \n][^\n]', "add two newlines after '.. note::'"),
283 (r'(^| )pp +xxxxqq[ \n][^\n]', "add two newlines after '.. note::'"),
267 284 ]
268 285 ]
269 286
270 287 pyfilters = [
271 288 (r"""(?msx)(?P<comment>\#.*?$)|
272 289 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
273 290 (?P<text>(([^\\]|\\.)*?))
274 291 (?P=quote))""", reppython),
275 292 ]
276 293
277 294 txtfilters = []
278 295
279 296 txtpats = [
280 297 [
281 298 ('\s$', 'trailing whitespace'),
282 299 ],
283 300 []
284 301 ]
285 302
286 303 cpats = [
287 304 [
288 305 (r'//', "don't use //-style comments"),
289 306 (r'^ ', "don't use spaces to indent"),
290 307 (r'\S\t', "don't use tabs except for indent"),
291 308 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
292 309 (r'.{81}', "line too long"),
293 310 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
294 311 (r'return\(', "return is not a function"),
295 312 (r' ;', "no space before ;"),
296 313 (r'[)][{]', "space between ) and {"),
297 314 (r'\w+\* \w+', "use int *foo, not int* foo"),
298 315 (r'\W\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
299 316 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
300 317 (r'\w,\w', "missing whitespace after ,"),
301 318 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
302 319 (r'^#\s+\w', "use #foo, not # foo"),
303 320 (r'[^\n]\Z', "no trailing newline"),
304 321 (r'^\s*#import\b', "use only #include in standard C code"),
305 322 ],
306 323 # warnings
307 324 []
308 325 ]
309 326
310 327 cfilters = [
311 328 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
312 329 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
313 330 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
314 331 (r'(\()([^)]+\))', repcallspaces),
315 332 ]
316 333
317 334 inutilpats = [
318 335 [
319 336 (r'\bui\.', "don't use ui in util"),
320 337 ],
321 338 # warnings
322 339 []
323 340 ]
324 341
325 342 inrevlogpats = [
326 343 [
327 344 (r'\brepo\.', "don't use repo in revlog"),
328 345 ],
329 346 # warnings
330 347 []
331 348 ]
332 349
333 350 checks = [
334 351 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
335 352 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
336 353 ('c', r'.*\.[ch]$', cfilters, cpats),
337 354 ('unified test', r'.*\.t$', utestfilters, utestpats),
338 355 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
339 356 inrevlogpats),
340 357 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
341 358 inutilpats),
342 359 ('txt', r'.*\.txt$', txtfilters, txtpats),
343 360 ]
344 361
345 362 def _preparepats():
346 363 for c in checks:
347 364 failandwarn = c[-1]
348 365 for pats in failandwarn:
349 366 for i, pseq in enumerate(pats):
350 367 # fix-up regexes for multi-line searches
351 368 p = pseq[0]
352 369 # \s doesn't match \n
353 370 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
354 371 # [^...] doesn't match newline
355 372 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
356 373
357 374 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
358 375 filters = c[2]
359 376 for i, flt in enumerate(filters):
360 377 filters[i] = re.compile(flt[0]), flt[1]
361 378 _preparepats()
362 379
363 380 class norepeatlogger(object):
364 381 def __init__(self):
365 382 self._lastseen = None
366 383
367 384 def log(self, fname, lineno, line, msg, blame):
368 385 """print error related a to given line of a given file.
369 386
370 387 The faulty line will also be printed but only once in the case
371 388 of multiple errors.
372 389
373 390 :fname: filename
374 391 :lineno: line number
375 392 :line: actual content of the line
376 393 :msg: error message
377 394 """
378 395 msgid = fname, lineno, line
379 396 if msgid != self._lastseen:
380 397 if blame:
381 398 print "%s:%d (%s):" % (fname, lineno, blame)
382 399 else:
383 400 print "%s:%d:" % (fname, lineno)
384 401 print " > %s" % line
385 402 self._lastseen = msgid
386 403 print " " + msg
387 404
388 405 _defaultlogger = norepeatlogger()
389 406
390 407 def getblame(f):
391 408 lines = []
392 409 for l in os.popen('hg annotate -un %s' % f):
393 410 start, line = l.split(':', 1)
394 411 user, rev = start.split()
395 412 lines.append((line[1:-1], user, rev))
396 413 return lines
397 414
398 415 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
399 416 blame=False, debug=False, lineno=True):
400 417 """checks style and portability of a given file
401 418
402 419 :f: filepath
403 420 :logfunc: function used to report error
404 421 logfunc(filename, linenumber, linecontent, errormessage)
405 422 :maxerr: number of error to display before aborting.
406 423 Set to false (default) to report all errors
407 424
408 425 return True if no error is found, False otherwise.
409 426 """
410 427 blamecache = None
411 428 result = True
412 429 for name, match, filters, pats in checks:
413 430 if debug:
414 431 print name, f
415 432 fc = 0
416 433 if not re.match(match, f):
417 434 if debug:
418 435 print "Skipping %s for %s it doesn't match %s" % (
419 436 name, match, f)
420 437 continue
421 438 try:
422 439 fp = open(f)
423 440 except IOError, e:
424 441 print "Skipping %s, %s" % (f, str(e).split(':', 1)[0])
425 442 continue
426 443 pre = post = fp.read()
427 444 fp.close()
428 445 if "no-" "check-code" in pre:
429 446 if debug:
430 447 print "Skipping %s for %s it has no-" "check-code" % (
431 448 name, f)
432 449 break
433 450 for p, r in filters:
434 451 post = re.sub(p, r, post)
435 452 nerrs = len(pats[0]) # nerr elements are errors
436 453 if warnings:
437 454 pats = pats[0] + pats[1]
438 455 else:
439 456 pats = pats[0]
440 457 # print post # uncomment to show filtered version
441 458
442 459 if debug:
443 460 print "Checking %s for %s" % (name, f)
444 461
445 462 prelines = None
446 463 errors = []
447 464 for i, pat in enumerate(pats):
448 465 if len(pat) == 3:
449 466 p, msg, ignore = pat
450 467 else:
451 468 p, msg = pat
452 469 ignore = None
453 470
454 471 pos = 0
455 472 n = 0
456 473 for m in p.finditer(post):
457 474 if prelines is None:
458 475 prelines = pre.splitlines()
459 476 postlines = post.splitlines(True)
460 477
461 478 start = m.start()
462 479 while n < len(postlines):
463 480 step = len(postlines[n])
464 481 if pos + step > start:
465 482 break
466 483 pos += step
467 484 n += 1
468 485 l = prelines[n]
469 486
470 487 if "check-code" "-ignore" in l:
471 488 if debug:
472 489 print "Skipping %s for %s:%s (check-code" "-ignore)" % (
473 490 name, f, n)
474 491 continue
475 492 elif ignore and re.search(ignore, l, re.MULTILINE):
476 493 continue
477 494 bd = ""
478 495 if blame:
479 496 bd = 'working directory'
480 497 if not blamecache:
481 498 blamecache = getblame(f)
482 499 if n < len(blamecache):
483 500 bl, bu, br = blamecache[n]
484 501 if bl == l:
485 502 bd = '%s@%s' % (bu, br)
486 503 if i >= nerrs:
487 504 msg = "warning: " + msg
488 505 errors.append((f, lineno and n + 1, l, msg, bd))
489 506 result = False
490 507
491 508 errors.sort()
492 509 for e in errors:
493 510 logfunc(*e)
494 511 fc += 1
495 512 if maxerr and fc >= maxerr:
496 513 print " (too many errors, giving up)"
497 514 break
498 515
499 516 return result
500 517
501 518 if __name__ == "__main__":
502 519 parser = optparse.OptionParser("%prog [options] [files]")
503 520 parser.add_option("-w", "--warnings", action="store_true",
504 521 help="include warning-level checks")
505 522 parser.add_option("-p", "--per-file", type="int",
506 523 help="max warnings per file")
507 524 parser.add_option("-b", "--blame", action="store_true",
508 525 help="use annotate to generate blame info")
509 526 parser.add_option("", "--debug", action="store_true",
510 527 help="show debug information")
511 528 parser.add_option("", "--nolineno", action="store_false",
512 529 dest='lineno', help="don't show line numbers")
513 530
514 531 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
515 532 lineno=True)
516 533 (options, args) = parser.parse_args()
517 534
518 535 if len(args) == 0:
519 536 check = glob.glob("*")
520 537 else:
521 538 check = args
522 539
523 540 ret = 0
524 541 for f in check:
525 542 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
526 543 blame=options.blame, debug=options.debug,
527 544 lineno=options.lineno):
528 545 ret = 1
529 546 sys.exit(ret)
General Comments 0
You need to be logged in to leave comments. Login now