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