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