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