##// END OF EJS Templates
check-code: separate warnings to avoid repetitive str.startswith
Idan Kamara -
r14009:64de9ca6 default
parent child Browse files
Show More
@@ -1,310 +1,329 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # check-code - a style and portability checker for Mercurial
3 # check-code - a style and portability checker for Mercurial
4 #
4 #
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 import re, glob, os, sys
10 import re, glob, os, sys
11 import keyword
11 import keyword
12 import optparse
12 import optparse
13
13
14 def repquote(m):
14 def repquote(m):
15 t = re.sub(r"\w", "x", m.group('text'))
15 t = re.sub(r"\w", "x", m.group('text'))
16 t = re.sub(r"[^\sx]", "o", t)
16 t = re.sub(r"[^\sx]", "o", t)
17 return m.group('quote') + t + m.group('quote')
17 return m.group('quote') + t + m.group('quote')
18
18
19 def reppython(m):
19 def reppython(m):
20 comment = m.group('comment')
20 comment = m.group('comment')
21 if comment:
21 if comment:
22 return "#" * len(comment)
22 return "#" * len(comment)
23 return repquote(m)
23 return repquote(m)
24
24
25 def repcomment(m):
25 def repcomment(m):
26 return m.group(1) + "#" * len(m.group(2))
26 return m.group(1) + "#" * len(m.group(2))
27
27
28 def repccomment(m):
28 def repccomment(m):
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
30 return m.group(1) + t + "*/"
30 return m.group(1) + t + "*/"
31
31
32 def repcallspaces(m):
32 def repcallspaces(m):
33 t = re.sub(r"\n\s+", "\n", m.group(2))
33 t = re.sub(r"\n\s+", "\n", m.group(2))
34 return m.group(1) + t
34 return m.group(1) + t
35
35
36 def repinclude(m):
36 def repinclude(m):
37 return m.group(1) + "<foo>"
37 return m.group(1) + "<foo>"
38
38
39 def rephere(m):
39 def rephere(m):
40 t = re.sub(r"\S", "x", m.group(2))
40 t = re.sub(r"\S", "x", m.group(2))
41 return m.group(1) + t
41 return m.group(1) + t
42
42
43
43
44 testpats = [
44 testpats = [
45 [
45 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
46 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
46 (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
47 (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
47 (r'^function', "don't use 'function', use old style"),
48 (r'^function', "don't use 'function', use old style"),
48 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
49 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
49 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
50 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
50 (r'echo -n', "don't use 'echo -n', use printf"),
51 (r'echo -n', "don't use 'echo -n', use printf"),
51 (r'^diff.*-\w*N', "don't use 'diff -N'"),
52 (r'^diff.*-\w*N', "don't use 'diff -N'"),
52 (r'(^| )wc[^|]*$', "filter wc output"),
53 (r'(^| )wc[^|]*$', "filter wc output"),
53 (r'head -c', "don't use 'head -c', use 'dd'"),
54 (r'head -c', "don't use 'head -c', use 'dd'"),
54 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
55 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
55 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
56 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
56 (r'printf.*\\x', "don't use printf \\x, use Python"),
57 (r'printf.*\\x', "don't use printf \\x, use Python"),
57 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
58 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
58 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
59 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
59 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
60 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
60 "use egrep for extended grep syntax"),
61 "use egrep for extended grep syntax"),
61 (r'/bin/', "don't use explicit paths for tools"),
62 (r'/bin/', "don't use explicit paths for tools"),
62 (r'\$PWD', "don't use $PWD, use `pwd`"),
63 (r'\$PWD', "don't use $PWD, use `pwd`"),
63 (r'[^\n]\Z', "no trailing newline"),
64 (r'[^\n]\Z', "no trailing newline"),
64 (r'export.*=', "don't export and assign at once"),
65 (r'export.*=', "don't export and assign at once"),
65 ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
66 ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
66 (r'^source\b', "don't use 'source', use '.'"),
67 (r'^source\b', "don't use 'source', use '.'"),
67 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
68 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
68 (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
69 (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
69 (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
70 (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
71 ],
72 # warnings
73 []
70 ]
74 ]
71
75
72 testfilters = [
76 testfilters = [
73 (r"( *)(#([^\n]*\S)?)", repcomment),
77 (r"( *)(#([^\n]*\S)?)", repcomment),
74 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
78 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
75 ]
79 ]
76
80
77 uprefix = r"^ \$ "
81 uprefix = r"^ \$ "
78 uprefixc = r"^ > "
82 uprefixc = r"^ > "
79 utestpats = [
83 utestpats = [
84 [
80 (r'^(\S| $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
85 (r'^(\S| $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"),
81 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
86 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
82 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
87 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
83 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
88 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
84 (uprefix + r'.*\|\| echo.*(fail|error)',
89 (uprefix + r'.*\|\| echo.*(fail|error)',
85 "explicit exit code checks unnecessary"),
90 "explicit exit code checks unnecessary"),
86 (uprefix + r'set -e', "don't use set -e"),
91 (uprefix + r'set -e', "don't use set -e"),
87 (uprefixc + r'( *)\t', "don't use tabs to indent"),
92 (uprefixc + r'( *)\t', "don't use tabs to indent"),
93 ],
94 # warnings
95 []
88 ]
96 ]
89
97
90 for p, m in testpats:
98 for p, m in testpats[0] + testpats[1]:
91 if p.startswith('^'):
99 if p.startswith('^'):
92 p = uprefix + p[1:]
100 p = uprefix + p[1:]
93 else:
101 else:
94 p = uprefix + p
102 p = uprefix + p
95 utestpats.append((p, m))
103 utestpats.append((p, m))
96
104
97 utestfilters = [
105 utestfilters = [
98 (r"( *)(#([^\n]*\S)?)", repcomment),
106 (r"( *)(#([^\n]*\S)?)", repcomment),
99 ]
107 ]
100
108
101 pypats = [
109 pypats = [
110 [
102 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
111 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
103 "tuple parameter unpacking not available in Python 3+"),
112 "tuple parameter unpacking not available in Python 3+"),
104 (r'lambda\s*\(.*,.*\)',
113 (r'lambda\s*\(.*,.*\)',
105 "tuple parameter unpacking not available in Python 3+"),
114 "tuple parameter unpacking not available in Python 3+"),
106 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
115 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
107 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
116 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
108 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
117 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
109 (r'^\s*\t', "don't use tabs"),
118 (r'^\s*\t', "don't use tabs"),
110 (r'\S;\s*\n', "semicolon"),
119 (r'\S;\s*\n', "semicolon"),
111 (r'\w,\w', "missing whitespace after ,"),
120 (r'\w,\w', "missing whitespace after ,"),
112 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
121 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
113 (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
122 (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
114 (r'.{85}', "line too long"),
123 (r'.{85}', "line too long"),
115 (r'.{81}', "warning: line over 80 characters"),
116 (r'[^\n]\Z', "no trailing newline"),
124 (r'[^\n]\Z', "no trailing newline"),
117 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
125 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
118 # (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
126 # (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
119 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
127 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
120 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
128 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
121 "linebreak after :"),
129 "linebreak after :"),
122 (r'class\s[^(]:', "old-style class, use class foo(object)"),
130 (r'class\s[^(]:', "old-style class, use class foo(object)"),
123 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
131 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
124 "Python keyword is not a function"),
132 "Python keyword is not a function"),
125 (r',]', "unneeded trailing ',' in list"),
133 (r',]', "unneeded trailing ',' in list"),
126 # (r'class\s[A-Z][^\(]*\((?!Exception)',
134 # (r'class\s[A-Z][^\(]*\((?!Exception)',
127 # "don't capitalize non-exception classes"),
135 # "don't capitalize non-exception classes"),
128 # (r'in range\(', "use xrange"),
136 # (r'in range\(', "use xrange"),
129 # (r'^\s*print\s+', "avoid using print in core and extensions"),
137 # (r'^\s*print\s+', "avoid using print in core and extensions"),
130 (r'[\x80-\xff]', "non-ASCII character literal"),
138 (r'[\x80-\xff]', "non-ASCII character literal"),
131 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
139 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
132 (r'^\s*with\s+', "with not available in Python 2.4"),
140 (r'^\s*with\s+', "with not available in Python 2.4"),
133 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
141 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
134 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
142 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
135 (r'(?<!def)\s+(any|all|format)\(',
143 (r'(?<!def)\s+(any|all|format)\(',
136 "any/all/format not available in Python 2.4"),
144 "any/all/format not available in Python 2.4"),
137 (r'(?<!def)\s+(callable)\(',
145 (r'(?<!def)\s+(callable)\(',
138 "callable not available in Python 3, use hasattr(f, '__call__')"),
146 "callable not available in Python 3, use hasattr(f, '__call__')"),
139 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
147 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
140 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
148 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
141 "gratuitous whitespace after Python keyword"),
149 "gratuitous whitespace after Python keyword"),
142 (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"),
150 (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"),
143 # (r'\s\s=', "gratuitous whitespace before ="),
151 # (r'\s\s=', "gratuitous whitespace before ="),
144 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
152 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
145 "missing whitespace around operator"),
153 "missing whitespace around operator"),
146 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
154 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
147 "missing whitespace around operator"),
155 "missing whitespace around operator"),
148 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
156 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
149 "missing whitespace around operator"),
157 "missing whitespace around operator"),
150 (r'[^+=*!<>&| -](\s=|=\s)[^= ]',
158 (r'[^+=*!<>&| -](\s=|=\s)[^= ]',
151 "wrong whitespace around ="),
159 "wrong whitespace around ="),
152 (r'raise Exception', "don't raise generic exceptions"),
160 (r'raise Exception', "don't raise generic exceptions"),
161 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
162 (r' [=!]=\s+(True|False|None)',
163 "comparison with singleton, use 'is' or 'is not' instead"),
164 ],
165 # warnings
166 [
167 (r'.{81}', "warning: line over 80 characters"),
153 (r'^\s*except:$', "warning: naked except clause"),
168 (r'^\s*except:$', "warning: naked except clause"),
154 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
169 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
155 "warning: unwrapped ui message"),
170 "warning: unwrapped ui message"),
156 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
171 ]
157 (r' [=!]=\s+(True|False|None)',
158 "comparison with singleton, use 'is' or 'is not' instead"),
159 ]
172 ]
160
173
161 pyfilters = [
174 pyfilters = [
162 (r"""(?msx)(?P<comment>\#.*?$)|
175 (r"""(?msx)(?P<comment>\#.*?$)|
163 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
176 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
164 (?P<text>(([^\\]|\\.)*?))
177 (?P<text>(([^\\]|\\.)*?))
165 (?P=quote))""", reppython),
178 (?P=quote))""", reppython),
166 ]
179 ]
167
180
168 cpats = [
181 cpats = [
182 [
169 (r'//', "don't use //-style comments"),
183 (r'//', "don't use //-style comments"),
170 (r'^ ', "don't use spaces to indent"),
184 (r'^ ', "don't use spaces to indent"),
171 (r'\S\t', "don't use tabs except for indent"),
185 (r'\S\t', "don't use tabs except for indent"),
172 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
186 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
173 (r'.{85}', "line too long"),
187 (r'.{85}', "line too long"),
174 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
188 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
175 (r'return\(', "return is not a function"),
189 (r'return\(', "return is not a function"),
176 (r' ;', "no space before ;"),
190 (r' ;', "no space before ;"),
177 (r'\w+\* \w+', "use int *foo, not int* foo"),
191 (r'\w+\* \w+', "use int *foo, not int* foo"),
178 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
192 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
179 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
193 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
180 (r'\w,\w', "missing whitespace after ,"),
194 (r'\w,\w', "missing whitespace after ,"),
181 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
195 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
182 (r'^#\s+\w', "use #foo, not # foo"),
196 (r'^#\s+\w', "use #foo, not # foo"),
183 (r'[^\n]\Z', "no trailing newline"),
197 (r'[^\n]\Z', "no trailing newline"),
184 (r'^\s*#import\b', "use only #include in standard C code"),
198 (r'^\s*#import\b', "use only #include in standard C code"),
199 ],
200 # warnings
201 []
185 ]
202 ]
186
203
187 cfilters = [
204 cfilters = [
188 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
205 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
189 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
206 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
190 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
207 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
191 (r'(\()([^)]+\))', repcallspaces),
208 (r'(\()([^)]+\))', repcallspaces),
192 ]
209 ]
193
210
194 checks = [
211 checks = [
195 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
212 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
196 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
213 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
197 ('c', r'.*\.c$', cfilters, cpats),
214 ('c', r'.*\.c$', cfilters, cpats),
198 ('unified test', r'.*\.t$', utestfilters, utestpats),
215 ('unified test', r'.*\.t$', utestfilters, utestpats),
199 ]
216 ]
200
217
201 class norepeatlogger(object):
218 class norepeatlogger(object):
202 def __init__(self):
219 def __init__(self):
203 self._lastseen = None
220 self._lastseen = None
204
221
205 def log(self, fname, lineno, line, msg, blame):
222 def log(self, fname, lineno, line, msg, blame):
206 """print error related a to given line of a given file.
223 """print error related a to given line of a given file.
207
224
208 The faulty line will also be printed but only once in the case
225 The faulty line will also be printed but only once in the case
209 of multiple errors.
226 of multiple errors.
210
227
211 :fname: filename
228 :fname: filename
212 :lineno: line number
229 :lineno: line number
213 :line: actual content of the line
230 :line: actual content of the line
214 :msg: error message
231 :msg: error message
215 """
232 """
216 msgid = fname, lineno, line
233 msgid = fname, lineno, line
217 if msgid != self._lastseen:
234 if msgid != self._lastseen:
218 if blame:
235 if blame:
219 print "%s:%d (%s):" % (fname, lineno, blame)
236 print "%s:%d (%s):" % (fname, lineno, blame)
220 else:
237 else:
221 print "%s:%d:" % (fname, lineno)
238 print "%s:%d:" % (fname, lineno)
222 print " > %s" % line
239 print " > %s" % line
223 self._lastseen = msgid
240 self._lastseen = msgid
224 print " " + msg
241 print " " + msg
225
242
226 _defaultlogger = norepeatlogger()
243 _defaultlogger = norepeatlogger()
227
244
228 def getblame(f):
245 def getblame(f):
229 lines = []
246 lines = []
230 for l in os.popen('hg annotate -un %s' % f):
247 for l in os.popen('hg annotate -un %s' % f):
231 start, line = l.split(':', 1)
248 start, line = l.split(':', 1)
232 user, rev = start.split()
249 user, rev = start.split()
233 lines.append((line[1:-1], user, rev))
250 lines.append((line[1:-1], user, rev))
234 return lines
251 return lines
235
252
236 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
253 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
237 blame=False):
254 blame=False):
238 """checks style and portability of a given file
255 """checks style and portability of a given file
239
256
240 :f: filepath
257 :f: filepath
241 :logfunc: function used to report error
258 :logfunc: function used to report error
242 logfunc(filename, linenumber, linecontent, errormessage)
259 logfunc(filename, linenumber, linecontent, errormessage)
243 :maxerr: number of error to display before arborting.
260 :maxerr: number of error to display before arborting.
244 Set to None (default) to report all errors
261 Set to None (default) to report all errors
245
262
246 return True if no error is found, False otherwise.
263 return True if no error is found, False otherwise.
247 """
264 """
248 blamecache = None
265 blamecache = None
249 result = True
266 result = True
250 for name, match, filters, pats in checks:
267 for name, match, filters, pats in checks:
251 fc = 0
268 fc = 0
252 if not re.match(match, f):
269 if not re.match(match, f):
253 continue
270 continue
254 fp = open(f)
271 fp = open(f)
255 pre = post = fp.read()
272 pre = post = fp.read()
256 fp.close()
273 fp.close()
257 if "no-" + "check-code" in pre:
274 if "no-" + "check-code" in pre:
258 break
275 break
259 for p, r in filters:
276 for p, r in filters:
260 post = re.sub(p, r, post)
277 post = re.sub(p, r, post)
278 if warnings:
279 pats = pats[0] + pats[1]
280 else:
281 pats = pats[0]
261 # print post # uncomment to show filtered version
282 # print post # uncomment to show filtered version
262 z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
283 z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
263 for n, l in z:
284 for n, l in z:
264 if "check-code" + "-ignore" in l[0]:
285 if "check-code" + "-ignore" in l[0]:
265 continue
286 continue
266 for p, msg in pats:
287 for p, msg in pats:
267 if not warnings and msg.startswith("warning"):
268 continue
269 if re.search(p, l[1]):
288 if re.search(p, l[1]):
270 bd = ""
289 bd = ""
271 if blame:
290 if blame:
272 bd = 'working directory'
291 bd = 'working directory'
273 if not blamecache:
292 if not blamecache:
274 blamecache = getblame(f)
293 blamecache = getblame(f)
275 if n < len(blamecache):
294 if n < len(blamecache):
276 bl, bu, br = blamecache[n]
295 bl, bu, br = blamecache[n]
277 if bl == l[0]:
296 if bl == l[0]:
278 bd = '%s@%s' % (bu, br)
297 bd = '%s@%s' % (bu, br)
279 logfunc(f, n + 1, l[0], msg, bd)
298 logfunc(f, n + 1, l[0], msg, bd)
280 fc += 1
299 fc += 1
281 result = False
300 result = False
282 if maxerr is not None and fc >= maxerr:
301 if maxerr is not None and fc >= maxerr:
283 print " (too many errors, giving up)"
302 print " (too many errors, giving up)"
284 break
303 break
285 break
304 break
286 return result
305 return result
287
306
288 if __name__ == "__main__":
307 if __name__ == "__main__":
289 parser = optparse.OptionParser("%prog [options] [files]")
308 parser = optparse.OptionParser("%prog [options] [files]")
290 parser.add_option("-w", "--warnings", action="store_true",
309 parser.add_option("-w", "--warnings", action="store_true",
291 help="include warning-level checks")
310 help="include warning-level checks")
292 parser.add_option("-p", "--per-file", type="int",
311 parser.add_option("-p", "--per-file", type="int",
293 help="max warnings per file")
312 help="max warnings per file")
294 parser.add_option("-b", "--blame", action="store_true",
313 parser.add_option("-b", "--blame", action="store_true",
295 help="use annotate to generate blame info")
314 help="use annotate to generate blame info")
296
315
297 parser.set_defaults(per_file=15, warnings=False, blame=False)
316 parser.set_defaults(per_file=15, warnings=False, blame=False)
298 (options, args) = parser.parse_args()
317 (options, args) = parser.parse_args()
299
318
300 if len(args) == 0:
319 if len(args) == 0:
301 check = glob.glob("*")
320 check = glob.glob("*")
302 else:
321 else:
303 check = args
322 check = args
304
323
305 for f in check:
324 for f in check:
306 ret = 0
325 ret = 0
307 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
326 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
308 blame=options.blame):
327 blame=options.blame):
309 ret = 1
328 ret = 1
310 sys.exit(ret)
329 sys.exit(ret)
General Comments 0
You need to be logged in to leave comments. Login now