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