260 # more lines at the same indent level |
261 # more lines at the same indent level |
261 r'((?P=indent)[^\n]+\n)*' |
262 r'((?P=indent)[^\n]+\n)*' |
262 # a pass at the same indent level, which is bogus |
263 # a pass at the same indent level, which is bogus |
263 r'(?P=indent)pass[ \t\n#]' |
264 r'(?P=indent)pass[ \t\n#]' |
264 ), 'omit superfluous pass'), |
265 ), 'omit superfluous pass'), |
265 (r'.{81}', "line too long"), |
|
266 (r'[^\n]\Z', "no trailing newline"), |
266 (r'[^\n]\Z', "no trailing newline"), |
267 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"), |
267 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"), |
268 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', |
268 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', |
269 # "don't use underbars in identifiers"), |
269 # "don't use underbars in identifiers"), |
270 (r'^\s+(self\.)?[A-Za-z][a-z0-9]+[A-Z]\w* = ', |
270 (r'^\s+(self\.)?[A-Za-z][a-z0-9]+[A-Z]\w* = ', |
298 "missing whitespace around operator"), |
298 "missing whitespace around operator"), |
299 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]', |
299 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]', |
300 "wrong whitespace around ="), |
300 "wrong whitespace around ="), |
301 (r'\([^()]*( =[^=]|[^<>!=]= )', |
301 (r'\([^()]*( =[^=]|[^<>!=]= )', |
302 "no whitespace around = for named parameters"), |
302 "no whitespace around = for named parameters"), |
303 (r'raise Exception', "don't raise generic exceptions"), |
|
304 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$', |
303 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$', |
305 "don't use old-style two-argument raise, use Exception(message)"), |
304 "don't use old-style two-argument raise, use Exception(message)"), |
306 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"), |
305 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"), |
307 (r' [=!]=\s+(True|False|None)', |
306 (r' [=!]=\s+(True|False|None)', |
308 "comparison with singleton, use 'is' or 'is not' instead"), |
307 "comparison with singleton, use 'is' or 'is not' instead"), |
314 'instead', r'#.*hasattr-py3-only'), |
313 'instead', r'#.*hasattr-py3-only'), |
315 (r'opener\([^)]*\).read\(', |
314 (r'opener\([^)]*\).read\(', |
316 "use opener.read() instead"), |
315 "use opener.read() instead"), |
317 (r'opener\([^)]*\).write\(', |
316 (r'opener\([^)]*\).write\(', |
318 "use opener.write() instead"), |
317 "use opener.write() instead"), |
319 (r'[\s\(](open|file)\([^)]*\)\.read\(', |
|
320 "use util.readfile() instead"), |
|
321 (r'[\s\(](open|file)\([^)]*\)\.write\(', |
|
322 "use util.writefile() instead"), |
|
323 (r'^[\s\(]*(open(er)?|file)\([^)]*\)(?!\.close\(\))', |
|
324 "always assign an opened file to a variable, and close it afterwards"), |
|
325 (r'[\s\(](open|file)\([^)]*\)\.(?!close\(\))', |
|
326 "always assign an opened file to a variable, and close it afterwards"), |
|
327 (r'(?i)descend[e]nt', "the proper spelling is descendAnt"), |
318 (r'(?i)descend[e]nt', "the proper spelling is descendAnt"), |
328 (r'\.debug\(\_', "don't mark debug messages for translation"), |
319 (r'\.debug\(\_', "don't mark debug messages for translation"), |
329 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"), |
320 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"), |
330 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'), |
321 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'), |
331 (r'^\s*except\s([^\(,]+|\([^\)]+\))\s*,', |
322 (r'^\s*except\s([^\(,]+|\([^\)]+\))\s*,', |
332 'legacy exception syntax; use "as" instead of ","'), |
323 'legacy exception syntax; use "as" instead of ","'), |
333 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"), |
|
334 (r'release\(.*wlock, .*lock\)', "wrong lock release order"), |
324 (r'release\(.*wlock, .*lock\)', "wrong lock release order"), |
335 (r'\bdef\s+__bool__\b', "__bool__ should be __nonzero__ in Python 2"), |
325 (r'\bdef\s+__bool__\b', "__bool__ should be __nonzero__ in Python 2"), |
336 (r'os\.path\.join\(.*, *(""|\'\')\)', |
326 (r'os\.path\.join\(.*, *(""|\'\')\)', |
337 "use pathutil.normasprefix(path) instead of os.path.join(path, '')"), |
327 "use pathutil.normasprefix(path) instead of os.path.join(path, '')"), |
338 (r'\s0[0-7]+\b', 'legacy octal syntax; use "0o" prefix instead of "0"'), |
328 (r'\s0[0-7]+\b', 'legacy octal syntax; use "0o" prefix instead of "0"'), |
339 # XXX only catch mutable arguments on the first line of the definition |
329 # XXX only catch mutable arguments on the first line of the definition |
340 (r'def.*[( ]\w+=\{\}', "don't use mutable default arguments"), |
330 (r'def.*[( ]\w+=\{\}', "don't use mutable default arguments"), |
341 (r'\butil\.Abort\b', "directly use error.Abort"), |
331 (r'\butil\.Abort\b', "directly use error.Abort"), |
342 (r'^@(\w*\.)?cachefunc', "module-level @cachefunc is risky, please avoid"), |
332 (r'^@(\w*\.)?cachefunc', "module-level @cachefunc is risky, please avoid"), |
343 (r'^import atexit', "don't use atexit, use ui.atexit"), |
|
344 (r'^import Queue', "don't use Queue, use pycompat.queue.Queue + " |
333 (r'^import Queue', "don't use Queue, use pycompat.queue.Queue + " |
345 "pycompat.queue.Empty"), |
334 "pycompat.queue.Empty"), |
346 (r'^import cStringIO', "don't use cStringIO.StringIO, use util.stringio"), |
335 (r'^import cStringIO', "don't use cStringIO.StringIO, use util.stringio"), |
347 (r'^import urllib', "don't use urllib, use util.urlreq/util.urlerr"), |
336 (r'^import urllib', "don't use urllib, use util.urlreq/util.urlerr"), |
348 (r'^import SocketServer', "don't use SockerServer, use util.socketserver"), |
337 (r'^import SocketServer', "don't use SockerServer, use util.socketserver"), |
356 "use mercurial.policy.importmod instead"), |
345 "use mercurial.policy.importmod instead"), |
357 (r'\.next\(\)', "don't use .next(), use next(...)"), |
346 (r'\.next\(\)', "don't use .next(), use next(...)"), |
358 (r'([a-z]*).revision\(\1\.node\(', |
347 (r'([a-z]*).revision\(\1\.node\(', |
359 "don't convert rev to node before passing to revision(nodeorrev)"), |
348 "don't convert rev to node before passing to revision(nodeorrev)"), |
360 (r'platform\.system\(\)', "don't use platform.system(), use pycompat"), |
349 (r'platform\.system\(\)', "don't use platform.system(), use pycompat"), |
|
350 |
|
351 ], |
|
352 # warnings |
|
353 [ |
|
354 ] |
|
355 ] |
|
356 |
|
357 # patterns to check normal *.py files |
|
358 pypats = [ |
|
359 [ |
|
360 # Ideally, these should be placed in "commonpypats" for |
|
361 # consistency of coding rules in Mercurial source tree. |
|
362 # But on the other hand, these are not so seriously required for |
|
363 # python code fragments embedded in test scripts. Fixing test |
|
364 # scripts for these patterns requires many changes, and has less |
|
365 # profit than effort. |
|
366 (r'.{81}', "line too long"), |
|
367 (r'raise Exception', "don't raise generic exceptions"), |
|
368 (r'[\s\(](open|file)\([^)]*\)\.read\(', |
|
369 "use util.readfile() instead"), |
|
370 (r'[\s\(](open|file)\([^)]*\)\.write\(', |
|
371 "use util.writefile() instead"), |
|
372 (r'^[\s\(]*(open(er)?|file)\([^)]*\)(?!\.close\(\))', |
|
373 "always assign an opened file to a variable, and close it afterwards"), |
|
374 (r'[\s\(](open|file)\([^)]*\)\.(?!close\(\))', |
|
375 "always assign an opened file to a variable, and close it afterwards"), |
|
376 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"), |
|
377 (r'^import atexit', "don't use atexit, use ui.atexit"), |
361 |
378 |
362 # rules depending on implementation of repquote() |
379 # rules depending on implementation of repquote() |
363 (r' x+[xpqo%APM][\'"]\n\s+[\'"]x', |
380 (r' x+[xpqo%APM][\'"]\n\s+[\'"]x', |
364 'string join across lines with no space'), |
381 'string join across lines with no space'), |
365 (r'''(?x)ui\.(status|progress|write|note|warn)\( |
382 (r'''(?x)ui\.(status|progress|write|note|warn)\( |
375 ((%([ n]?[PM]?([np]+|A))?x)|%%|b[bnx]|[ \nnpqAPMo])*x |
392 ((%([ n]?[PM]?([np]+|A))?x)|%%|b[bnx]|[ \nnpqAPMo])*x |
376 (?# this regexp can't use [^...] style, |
393 (?# this regexp can't use [^...] style, |
377 # because _preparepats forcibly adds "\n" into [^...], |
394 # because _preparepats forcibly adds "\n" into [^...], |
378 # even though this regexp wants match it against "\n")''', |
395 # even though this regexp wants match it against "\n")''', |
379 "missing _() in ui message (use () to hide false-positives)"), |
396 "missing _() in ui message (use () to hide false-positives)"), |
380 ], |
397 ] + commonpypats[0], |
381 # warnings |
398 # warnings |
382 [ |
399 [ |
383 # rules depending on implementation of repquote() |
400 # rules depending on implementation of repquote() |
384 (r'(^| )pp +xxxxqq[ \n][^\n]', "add two newlines after '.. note::'"), |
401 (r'(^| )pp +xxxxqq[ \n][^\n]', "add two newlines after '.. note::'"), |
385 ] |
402 ] + commonpypats[1] |
386 ] |
403 ] |
387 |
404 |
388 pyfilters = [ |
405 # common filters to convert *.py |
|
406 commonpyfilters = [ |
389 (r"""(?msx)(?P<comment>\#.*?$)| |
407 (r"""(?msx)(?P<comment>\#.*?$)| |
390 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!"))) |
408 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!"))) |
391 (?P<text>(([^\\]|\\.)*?)) |
409 (?P<text>(([^\\]|\\.)*?)) |
392 (?P=quote))""", reppython), |
410 (?P=quote))""", reppython), |
393 ] |
411 ] |
|
412 |
|
413 # filters to convert normal *.py files |
|
414 pyfilters = [ |
|
415 ] + commonpyfilters |
394 |
416 |
395 # non-filter patterns |
417 # non-filter patterns |
396 pynfpats = [ |
418 pynfpats = [ |
397 [ |
419 [ |
398 (r'pycompat\.osname\s*[=!]=\s*[\'"]nt[\'"]', "use pycompat.iswindows"), |
420 (r'pycompat\.osname\s*[=!]=\s*[\'"]nt[\'"]', "use pycompat.iswindows"), |