##// END OF EJS Templates
py3kcompat: added a "compatibility layer" for py3k...
py3kcompat: added a "compatibility layer" for py3k This patch adds some ugly constructs. The first of them is bytesformatter, a function that formats strings like when '%' is called. The main motivation for this function is py3k's strange behavior: >>> 'foo %s' % b'bar' "foo b'bar'" >>> b'foo %s' % b'bar' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for %: 'bytes' and 'bytes' >>> b'foo %s' % 'bar' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for %: 'bytes' and 'str' In other words, if we can't format bytes with bytes, and recall that all mercurial strings will be converted by a fixer, then things will break badly if we don't take a similar approach. The other addition with this patch is that the os.environ dictionary is monkeypatched to have bytes items. Hopefully this won't be needed in the future, as python 3.2 might get a os.environb dictionary that holds bytes items.

File last commit:

r11672:dad18576 default
r11748:37a70a78 default
Show More
check-code.py
264 lines | 9.4 KiB | text/x-python | PythonLexer
Matt Mackall
Introduce check-code.py...
r10281 #!/usr/bin/env python
#
# check-code - a style and portability checker for Mercurial
#
Matt Mackall
check-code: fix copyright date
r10290 # Copyright 2010 Matt Mackall <mpm@selenic.com>
Matt Mackall
Introduce check-code.py...
r10281 #
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Matt Mackall
check-code: add --blame switch
r11604 import re, glob, os
Matt Mackall
check-code: add a warnings level...
r10895 import optparse
Matt Mackall
Introduce check-code.py...
r10281
def repquote(m):
Benoit Boissinot
check-code: improve quote detection regexp, add tests
r10722 t = re.sub(r"\w", "x", m.group('text'))
Matt Mackall
check-code: two more rules...
r10451 t = re.sub(r"[^\sx]", "o", t)
Benoit Boissinot
check-code: improve quote detection regexp, add tests
r10722 return m.group('quote') + t + m.group('quote')
Matt Mackall
Introduce check-code.py...
r10281
Benoit Boissinot
check-code: more tests and more robust python filtering
r10727 def reppython(m):
comment = m.group('comment')
if comment:
return "#" * len(comment)
return repquote(m)
Matt Mackall
Introduce check-code.py...
r10281
def repcomment(m):
return m.group(1) + "#" * len(m.group(2))
def repccomment(m):
t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
return m.group(1) + t + "*/"
def repcallspaces(m):
t = re.sub(r"\n\s+", "\n", m.group(2))
return m.group(1) + t
def repinclude(m):
return m.group(1) + "<foo>"
def rephere(m):
t = re.sub(r"\S", "x", m.group(2))
return m.group(1) + t
testpats = [
Martin Geisler
check-code.py: make help strings consistent
r10374 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
(r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
Matt Mackall
Introduce check-code.py...
r10281 (r'^function', "don't use 'function', use old style"),
Martin Geisler
check-code.py: make help strings consistent
r10374 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
Mads Kiilerich
check-code.py: escape backslash
r10373 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
Martin Geisler
check-code.py: make help strings consistent
r10374 (r'^diff.*-\w*N', "don't use 'diff -N'"),
Matt Mackall
Introduce check-code.py...
r10281 (r'(^| )wc[^|]*$', "filter wc output"),
Martin Geisler
check-code.py: make help strings consistent
r10374 (r'head -c', "don't use 'head -c', use 'dd'"),
(r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
(r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
(r'printf.*\\x', "don't use printf \\x, use Python"),
Matt Mackall
Introduce check-code.py...
r10281 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
(r'rm -rf \*', "don't use naked rm -rf, target a directory"),
(r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
"use egrep for extended grep syntax"),
(r'/bin/', "don't use explicit paths for tools"),
(r'\$PWD', "don't use $PWD, use `pwd`"),
(r'[^\n]\Z', "no trailing newline"),
Mads Kiilerich
test-merge-default and check-code.py: No "export x=x" in sh
r10658 (r'export.*=', "don't export and assign at once"),
Mads Kiilerich
check-code.py: Check for bare ^...
r10802 ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
Yuya Nishihara
check-code: add check for 'source'
r11210 (r'^source\b', "don't use 'source', use '.'"),
Matt Mackall
Introduce check-code.py...
r10281 ]
testfilters = [
(r"( *)(#([^\n]*\S)?)", repcomment),
(r"<<(\S+)((.|\n)*?\n\1)", rephere),
]
pypats = [
Renato Cunha
check-code: check for tuple parameter unpacking (missing in py3k)
r11568 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
"tuple parameter unpacking not available in Python 3+"),
(r'lambda\s*\(.*,.*\)',
"tuple parameter unpacking not available in Python 3+"),
Renato Cunha
check-code: added check for reduce usage
r11569 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
Martin Geisler
check-code: catch dict.has_key
r11602 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
Matt Mackall
Introduce check-code.py...
r10281 (r'^\s*\t', "don't use tabs"),
Matt Mackall
check-code: import some pylint checks
r10412 (r'\S;\s*\n', "semicolon"),
Matt Mackall
Introduce check-code.py...
r10281 (r'\w,\w', "missing whitespace after ,"),
(r'\w[+/*\-<>]\w', "missing whitespace in expression"),
(r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
(r'.{85}', "line too long"),
Matt Mackall
check-code: add warning on lines over 80 characters
r11672 (r'.{81}', "warning: line over 80 characters"),
Matt Mackall
Introduce check-code.py...
r10281 (r'[^\n]\Z', "no trailing newline"),
# (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
# (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
Matt Mackall
check-code: check thyself
r10286 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
"linebreak after :"),
Matt Mackall
Introduce check-code.py...
r10281 (r'class\s[^(]:', "old-style class, use class foo(object)"),
Matt Mackall
check-code: del isn't a function
r10291 (r'^\s+del\(', "del isn't a function"),
Matt Mackall
Introduce check-code.py...
r10281 (r'^\s+except\(', "except isn't a function"),
Matt Mackall
check-code: import some pylint checks
r10412 (r',]', "unneeded trailing ',' in list"),
Matt Mackall
Introduce check-code.py...
r10281 # (r'class\s[A-Z][^\(]*\((?!Exception)',
# "don't capitalize non-exception classes"),
# (r'in range\(', "use xrange"),
# (r'^\s*print\s+', "avoid using print in core and extensions"),
(r'[\x80-\xff]', "non-ASCII character literal"),
(r'("\')\.format\(', "str.format() not available in Python 2.4"),
(r'^\s*with\s+', "with not available in Python 2.4"),
Martin Geisler
check-code: reformat long lines
r11345 (r'(?<!def)\s+(any|all|format)\(',
"any/all/format not available in Python 2.4"),
Martin Geisler
check-code: add test for callable
r11522 (r'(?<!def)\s+(callable)\(',
"callable not available in Python 3, use hasattr(f, '__call__')"),
Matt Mackall
Introduce check-code.py...
r10281 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
(r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"),
# (r'\s\s=', "gratuitous whitespace before ="),
Martin Geisler
check-code: reformat long lines
r11345 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
"missing whitespace around operator"),
(r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
"missing whitespace around operator"),
(r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
"missing whitespace around operator"),
(r'[^+=*!<>&| -](\s=|=\s)[^= ]',
"wrong whitespace around ="),
Matt Mackall
check-code: two more rules...
r10451 (r'raise Exception', "don't raise generic exceptions"),
Martin Geisler
check-code: warn about untranslated ui.warn calls
r11599 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
Matt Mackall
check-code: add a warnings level...
r10895 "warning: unwrapped ui message"),
Matt Mackall
Introduce check-code.py...
r10281 ]
pyfilters = [
Benoit Boissinot
check-code: more tests and more robust python filtering
r10727 (r"""(?msx)(?P<comment>\#.*?$)|
((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
(?P<text>(([^\\]|\\.)*?))
(?P=quote))""", reppython),
Matt Mackall
Introduce check-code.py...
r10281 ]
cpats = [
(r'//', "don't use //-style comments"),
(r'^ ', "don't use spaces to indent"),
(r'\S\t', "don't use tabs except for indent"),
(r'(\S\s+|^\s+)\n', "trailing whitespace"),
(r'.{85}', "line too long"),
(r'(while|if|do|for)\(', "use space after while/if/do/for"),
(r'return\(', "return is not a function"),
(r' ;', "no space before ;"),
(r'\w+\* \w+', "use int *foo, not int* foo"),
(r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
(r'\S+ (\+\+|--)', "use foo++, not foo ++"),
(r'\w,\w', "missing whitespace after ,"),
(r'\w[+/*]\w', "missing whitespace in expression"),
(r'^#\s+\w', "use #foo, not # foo"),
(r'[^\n]\Z', "no trailing newline"),
]
cfilters = [
(r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
Benoit Boissinot
check-code: improve quote detection regexp, add tests
r10722 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
Matt Mackall
Introduce check-code.py...
r10281 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
(r'(\()([^)]+\))', repcallspaces),
]
checks = [
('python', r'.*\.(py|cgi)$', pyfilters, pypats),
('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
('c', r'.*\.c$', cfilters, cpats),
]
Pierre-Yves David
code-code: Add a logfunc argument to checkfile...
r10719 class norepeatlogger(object):
def __init__(self):
self._lastseen = None
Matt Mackall
check-code: add --blame switch
r11604 def log(self, fname, lineno, line, msg, blame):
Pierre-Yves David
code-code: Add a logfunc argument to checkfile...
r10719 """print error related a to given line of a given file.
The faulty line will also be printed but only once in the case
of multiple errors.
Matt Mackall
Introduce check-code.py...
r10281
Pierre-Yves David
code-code: Add a logfunc argument to checkfile...
r10719 :fname: filename
:lineno: line number
:line: actual content of the line
:msg: error message
"""
msgid = fname, lineno, line
if msgid != self._lastseen:
Matt Mackall
check-code: add --blame switch
r11604 if blame:
print "%s:%d (%s):" % (fname, lineno, blame)
else:
print "%s:%d:" % (fname, lineno)
Pierre-Yves David
code-code: Add a logfunc argument to checkfile...
r10719 print " > %s" % line
self._lastseen = msgid
print " " + msg
_defaultlogger = norepeatlogger()
Matt Mackall
check-code: add --blame switch
r11604 def getblame(f):
lines = []
for l in os.popen('hg annotate -un %s' % f):
start, line = l.split(':', 1)
user, rev = start.split()
lines.append((line[1:-1], user, rev))
return lines
def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
blame=False):
Pierre-Yves David
code-code: Add a logfunc argument to checkfile...
r10719 """checks style and portability of a given file
:f: filepath
:logfunc: function used to report error
logfunc(filename, linenumber, linecontent, errormessage)
:maxerr: number of error to display before arborting.
Set to None (default) to report all errors
Pierre-Yves David
check-code: add a return value to checkfile function...
r10720
return True if no error is found, False otherwise.
Pierre-Yves David
code-code: Add a logfunc argument to checkfile...
r10719 """
Matt Mackall
check-code: add --blame switch
r11604 blamecache = None
Pierre-Yves David
check-code: add a return value to checkfile function...
r10720 result = True
Matt Mackall
Introduce check-code.py...
r10281 for name, match, filters, pats in checks:
fc = 0
if not re.match(match, f):
continue
pre = post = open(f).read()
Matt Mackall
check-code: add some ignore hints
r10287 if "no-" + "check-code" in pre:
break
Matt Mackall
Introduce check-code.py...
r10281 for p, r in filters:
post = re.sub(p, r, post)
# print post # uncomment to show filtered version
z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
for n, l in z:
Matt Mackall
check-code: add some ignore hints
r10287 if "check-code" + "-ignore" in l[0]:
continue
Matt Mackall
Introduce check-code.py...
r10281 for p, msg in pats:
Matt Mackall
check-code: add a warnings level...
r10895 if not warnings and msg.startswith("warning"):
continue
Matt Mackall
Introduce check-code.py...
r10281 if re.search(p, l[1]):
Matt Mackall
check-code: add --blame switch
r11604 bd = ""
if blame:
bd = 'working directory'
if not blamecache:
blamecache = getblame(f)
if n < len(blamecache):
bl, bu, br = blamecache[n]
if bl == l[0]:
bd = '%s@%s' % (bu, br)
logfunc(f, n + 1, l[0], msg, bd)
Matt Mackall
Introduce check-code.py...
r10281 fc += 1
Pierre-Yves David
check-code: add a return value to checkfile function...
r10720 result = False
Pierre-Yves David
check-code: Add a ``maxerr`` argument to the ``checkfile`` function...
r10718 if maxerr is not None and fc >= maxerr:
Matt Mackall
Introduce check-code.py...
r10281 print " (too many errors, giving up)"
break
break
Pierre-Yves David
check-code: add a return value to checkfile function...
r10720 return result
Pierre-Yves David
check-code: Add a ``checkfile`` function...
r10717
Pierre-Yves David
check-code: Only call check-code if __name__ = "__main__"....
r10716 if __name__ == "__main__":
Matt Mackall
check-code: add a warnings level...
r10895 parser = optparse.OptionParser("%prog [options] [files]")
parser.add_option("-w", "--warnings", action="store_true",
help="include warning-level checks")
parser.add_option("-p", "--per-file", type="int",
help="max warnings per file")
Matt Mackall
check-code: add --blame switch
r11604 parser.add_option("-b", "--blame", action="store_true",
help="use annotate to generate blame info")
Matt Mackall
check-code: add a warnings level...
r10895
Matt Mackall
check-code: add --blame switch
r11604 parser.set_defaults(per_file=15, warnings=False, blame=False)
Matt Mackall
check-code: add a warnings level...
r10895 (options, args) = parser.parse_args()
if len(args) == 0:
Pierre-Yves David
check-code: Only call check-code if __name__ = "__main__"....
r10716 check = glob.glob("*")
else:
Matt Mackall
check-code: add a warnings level...
r10895 check = args
Matt Mackall
Introduce check-code.py...
r10281
Pierre-Yves David
check-code: Only call check-code if __name__ = "__main__"....
r10716 for f in check:
Matt Mackall
check-code: add --blame switch
r11604 checkfile(f, maxerr=options.per_file, warnings=options.warnings,
blame=options.blame)