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