##// END OF EJS Templates
check-code: do not abort on an unreadable file, only report this
Simon Heimberg -
r19494:3119dc15 stable
parent child Browse files
Show More
@@ -1,514 +1,518 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 try:
13 try:
14 import re2
14 import re2
15 except ImportError:
15 except ImportError:
16 re2 = None
16 re2 = None
17
17
18 def compilere(pat, multiline=False):
18 def compilere(pat, multiline=False):
19 if multiline:
19 if multiline:
20 pat = '(?m)' + pat
20 pat = '(?m)' + pat
21 if re2:
21 if re2:
22 try:
22 try:
23 return re2.compile(pat)
23 return re2.compile(pat)
24 except re2.error:
24 except re2.error:
25 pass
25 pass
26 return re.compile(pat)
26 return re.compile(pat)
27
27
28 def repquote(m):
28 def repquote(m):
29 t = re.sub(r"\w", "x", m.group('text'))
29 t = re.sub(r"\w", "x", m.group('text'))
30 t = re.sub(r"[^\s\nx]", "o", t)
30 t = re.sub(r"[^\s\nx]", "o", t)
31 return m.group('quote') + t + m.group('quote')
31 return m.group('quote') + t + m.group('quote')
32
32
33 def reppython(m):
33 def reppython(m):
34 comment = m.group('comment')
34 comment = m.group('comment')
35 if comment:
35 if comment:
36 l = len(comment.rstrip())
36 l = len(comment.rstrip())
37 return "#" * l + comment[l:]
37 return "#" * l + comment[l:]
38 return repquote(m)
38 return repquote(m)
39
39
40 def repcomment(m):
40 def repcomment(m):
41 return m.group(1) + "#" * len(m.group(2))
41 return m.group(1) + "#" * len(m.group(2))
42
42
43 def repccomment(m):
43 def repccomment(m):
44 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
44 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
45 return m.group(1) + t + "*/"
45 return m.group(1) + t + "*/"
46
46
47 def repcallspaces(m):
47 def repcallspaces(m):
48 t = re.sub(r"\n\s+", "\n", m.group(2))
48 t = re.sub(r"\n\s+", "\n", m.group(2))
49 return m.group(1) + t
49 return m.group(1) + t
50
50
51 def repinclude(m):
51 def repinclude(m):
52 return m.group(1) + "<foo>"
52 return m.group(1) + "<foo>"
53
53
54 def rephere(m):
54 def rephere(m):
55 t = re.sub(r"\S", "x", m.group(2))
55 t = re.sub(r"\S", "x", m.group(2))
56 return m.group(1) + t
56 return m.group(1) + t
57
57
58
58
59 testpats = [
59 testpats = [
60 [
60 [
61 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
61 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
62 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
62 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
63 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
63 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
64 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
64 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
65 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
65 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
66 (r'echo -n', "don't use 'echo -n', use printf"),
66 (r'echo -n', "don't use 'echo -n', use printf"),
67 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
67 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
68 (r'head -c', "don't use 'head -c', use 'dd'"),
68 (r'head -c', "don't use 'head -c', use 'dd'"),
69 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
69 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
70 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
70 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
71 (r'printf.*[^\\]\\([1-9]|0\d)', "don't use 'printf \NNN', use Python"),
71 (r'printf.*[^\\]\\([1-9]|0\d)', "don't use 'printf \NNN', use Python"),
72 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
72 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
73 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
73 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
74 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
74 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
75 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
75 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
76 "use egrep for extended grep syntax"),
76 "use egrep for extended grep syntax"),
77 (r'/bin/', "don't use explicit paths for tools"),
77 (r'/bin/', "don't use explicit paths for tools"),
78 (r'[^\n]\Z', "no trailing newline"),
78 (r'[^\n]\Z', "no trailing newline"),
79 (r'export.*=', "don't export and assign at once"),
79 (r'export.*=', "don't export and assign at once"),
80 (r'^source\b', "don't use 'source', use '.'"),
80 (r'^source\b', "don't use 'source', use '.'"),
81 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
81 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
82 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
82 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
83 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
83 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
84 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
84 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
85 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
85 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
86 (r'^alias\b.*=', "don't use alias, use a function"),
86 (r'^alias\b.*=', "don't use alias, use a function"),
87 (r'if\s*!', "don't use '!' to negate exit status"),
87 (r'if\s*!', "don't use '!' to negate exit status"),
88 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
88 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
89 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
89 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
90 (r'^( *)\t', "don't use tabs to indent"),
90 (r'^( *)\t', "don't use tabs to indent"),
91 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
91 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
92 "put a backslash-escaped newline after sed 'i' command"),
92 "put a backslash-escaped newline after sed 'i' command"),
93 ],
93 ],
94 # warnings
94 # warnings
95 [
95 [
96 (r'^function', "don't use 'function', use old style"),
96 (r'^function', "don't use 'function', use old style"),
97 (r'^diff.*-\w*N', "don't use 'diff -N'"),
97 (r'^diff.*-\w*N', "don't use 'diff -N'"),
98 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
98 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
99 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
99 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
100 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
100 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
101 ]
101 ]
102 ]
102 ]
103
103
104 testfilters = [
104 testfilters = [
105 (r"( *)(#([^\n]*\S)?)", repcomment),
105 (r"( *)(#([^\n]*\S)?)", repcomment),
106 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
106 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
107 ]
107 ]
108
108
109 winglobmsg = "use (glob) to match Windows paths too"
109 winglobmsg = "use (glob) to match Windows paths too"
110 uprefix = r"^ \$ "
110 uprefix = r"^ \$ "
111 utestpats = [
111 utestpats = [
112 [
112 [
113 (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
113 (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
114 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
114 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
115 "use regex test output patterns instead of sed"),
115 "use regex test output patterns instead of sed"),
116 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
116 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
117 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
117 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
118 (uprefix + r'.*\|\| echo.*(fail|error)',
118 (uprefix + r'.*\|\| echo.*(fail|error)',
119 "explicit exit code checks unnecessary"),
119 "explicit exit code checks unnecessary"),
120 (uprefix + r'set -e', "don't use set -e"),
120 (uprefix + r'set -e', "don't use set -e"),
121 (uprefix + r'\s', "don't indent commands, use > for continued lines"),
121 (uprefix + r'\s', "don't indent commands, use > for continued lines"),
122 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
122 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
123 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
123 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
124 winglobmsg),
124 winglobmsg),
125 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
125 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
126 (r'^ reverting .*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
126 (r'^ reverting .*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
127 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
127 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
128 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
128 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
129 (r'^ pushing subrepo \S+/\S+ to.*[^)]$', winglobmsg,
129 (r'^ pushing subrepo \S+/\S+ to.*[^)]$', winglobmsg,
130 '\$TESTTMP/unix-repo$'),
130 '\$TESTTMP/unix-repo$'),
131 (r'^ moving \S+/.*[^)]$', winglobmsg),
131 (r'^ moving \S+/.*[^)]$', winglobmsg),
132 (r'^ no changes made to subrepo since.*/.*[^)]$',
132 (r'^ no changes made to subrepo since.*/.*[^)]$',
133 winglobmsg, '\$TESTTMP/unix-repo$'),
133 winglobmsg, '\$TESTTMP/unix-repo$'),
134 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$',
134 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$',
135 winglobmsg, '\$TESTTMP/unix-repo$'),
135 winglobmsg, '\$TESTTMP/unix-repo$'),
136 ],
136 ],
137 # warnings
137 # warnings
138 [
138 [
139 (r'^ [^*?/\n]* \(glob\)$',
139 (r'^ [^*?/\n]* \(glob\)$',
140 "glob match with no glob character (?*/)"),
140 "glob match with no glob character (?*/)"),
141 ]
141 ]
142 ]
142 ]
143
143
144 for i in [0, 1]:
144 for i in [0, 1]:
145 for p, m in testpats[i]:
145 for p, m in testpats[i]:
146 if p.startswith(r'^'):
146 if p.startswith(r'^'):
147 p = r"^ [$>] (%s)" % p[1:]
147 p = r"^ [$>] (%s)" % p[1:]
148 else:
148 else:
149 p = r"^ [$>] .*(%s)" % p
149 p = r"^ [$>] .*(%s)" % p
150 utestpats[i].append((p, m))
150 utestpats[i].append((p, m))
151
151
152 utestfilters = [
152 utestfilters = [
153 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
153 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
154 (r"( *)(#([^\n]*\S)?)", repcomment),
154 (r"( *)(#([^\n]*\S)?)", repcomment),
155 ]
155 ]
156
156
157 pypats = [
157 pypats = [
158 [
158 [
159 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
159 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
160 "tuple parameter unpacking not available in Python 3+"),
160 "tuple parameter unpacking not available in Python 3+"),
161 (r'lambda\s*\(.*,.*\)',
161 (r'lambda\s*\(.*,.*\)',
162 "tuple parameter unpacking not available in Python 3+"),
162 "tuple parameter unpacking not available in Python 3+"),
163 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
163 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
164 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
164 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
165 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
165 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
166 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
166 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
167 (r'^\s*\t', "don't use tabs"),
167 (r'^\s*\t', "don't use tabs"),
168 (r'\S;\s*\n', "semicolon"),
168 (r'\S;\s*\n', "semicolon"),
169 (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"),
169 (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"),
170 (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"),
170 (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"),
171 (r'(\w|\)),\w', "missing whitespace after ,"),
171 (r'(\w|\)),\w', "missing whitespace after ,"),
172 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
172 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
173 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
173 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
174 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
174 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
175 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
175 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
176 (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
176 (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
177 r'((?:\n|\1\s.*\n)+?)\1finally:',
177 r'((?:\n|\1\s.*\n)+?)\1finally:',
178 'no yield inside try/finally in Python 2.4'),
178 'no yield inside try/finally in Python 2.4'),
179 (r'.{81}', "line too long"),
179 (r'.{81}', "line too long"),
180 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
180 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
181 (r'[^\n]\Z', "no trailing newline"),
181 (r'[^\n]\Z', "no trailing newline"),
182 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
182 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
183 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
183 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
184 # "don't use underbars in identifiers"),
184 # "don't use underbars in identifiers"),
185 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
185 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
186 "don't use camelcase in identifiers"),
186 "don't use camelcase in identifiers"),
187 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
187 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
188 "linebreak after :"),
188 "linebreak after :"),
189 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
189 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
190 (r'class\s[^( \n]+\(\):',
190 (r'class\s[^( \n]+\(\):',
191 "class foo() not available in Python 2.4, use class foo(object)"),
191 "class foo() not available in Python 2.4, use class foo(object)"),
192 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
192 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
193 "Python keyword is not a function"),
193 "Python keyword is not a function"),
194 (r',]', "unneeded trailing ',' in list"),
194 (r',]', "unneeded trailing ',' in list"),
195 # (r'class\s[A-Z][^\(]*\((?!Exception)',
195 # (r'class\s[A-Z][^\(]*\((?!Exception)',
196 # "don't capitalize non-exception classes"),
196 # "don't capitalize non-exception classes"),
197 # (r'in range\(', "use xrange"),
197 # (r'in range\(', "use xrange"),
198 # (r'^\s*print\s+', "avoid using print in core and extensions"),
198 # (r'^\s*print\s+', "avoid using print in core and extensions"),
199 (r'[\x80-\xff]', "non-ASCII character literal"),
199 (r'[\x80-\xff]', "non-ASCII character literal"),
200 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
200 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
201 (r'^\s*with\s+', "with not available in Python 2.4"),
201 (r'^\s*with\s+', "with not available in Python 2.4"),
202 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
202 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
203 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
203 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
204 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
204 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
205 (r'(?<!def)\s+(any|all|format)\(',
205 (r'(?<!def)\s+(any|all|format)\(',
206 "any/all/format not available in Python 2.4"),
206 "any/all/format not available in Python 2.4"),
207 (r'(?<!def)\s+(callable)\(',
207 (r'(?<!def)\s+(callable)\(',
208 "callable not available in Python 3, use getattr(f, '__call__', None)"),
208 "callable not available in Python 3, use getattr(f, '__call__', None)"),
209 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
209 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
210 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
210 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
211 "gratuitous whitespace after Python keyword"),
211 "gratuitous whitespace after Python keyword"),
212 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
212 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
213 # (r'\s\s=', "gratuitous whitespace before ="),
213 # (r'\s\s=', "gratuitous whitespace before ="),
214 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
214 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
215 "missing whitespace around operator"),
215 "missing whitespace around operator"),
216 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
216 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
217 "missing whitespace around operator"),
217 "missing whitespace around operator"),
218 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
218 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
219 "missing whitespace around operator"),
219 "missing whitespace around operator"),
220 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
220 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
221 "wrong whitespace around ="),
221 "wrong whitespace around ="),
222 (r'raise Exception', "don't raise generic exceptions"),
222 (r'raise Exception', "don't raise generic exceptions"),
223 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
223 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
224 "don't use old-style two-argument raise, use Exception(message)"),
224 "don't use old-style two-argument raise, use Exception(message)"),
225 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
225 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
226 (r' [=!]=\s+(True|False|None)',
226 (r' [=!]=\s+(True|False|None)',
227 "comparison with singleton, use 'is' or 'is not' instead"),
227 "comparison with singleton, use 'is' or 'is not' instead"),
228 (r'^\s*(while|if) [01]:',
228 (r'^\s*(while|if) [01]:',
229 "use True/False for constant Boolean expression"),
229 "use True/False for constant Boolean expression"),
230 (r'(?:(?<!def)\s+|\()hasattr',
230 (r'(?:(?<!def)\s+|\()hasattr',
231 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
231 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
232 (r'opener\([^)]*\).read\(',
232 (r'opener\([^)]*\).read\(',
233 "use opener.read() instead"),
233 "use opener.read() instead"),
234 (r'BaseException', 'not in Python 2.4, use Exception'),
234 (r'BaseException', 'not in Python 2.4, use Exception'),
235 (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
235 (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
236 (r'opener\([^)]*\).write\(',
236 (r'opener\([^)]*\).write\(',
237 "use opener.write() instead"),
237 "use opener.write() instead"),
238 (r'[\s\(](open|file)\([^)]*\)\.read\(',
238 (r'[\s\(](open|file)\([^)]*\)\.read\(',
239 "use util.readfile() instead"),
239 "use util.readfile() instead"),
240 (r'[\s\(](open|file)\([^)]*\)\.write\(',
240 (r'[\s\(](open|file)\([^)]*\)\.write\(',
241 "use util.readfile() instead"),
241 "use util.readfile() instead"),
242 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
242 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
243 "always assign an opened file to a variable, and close it afterwards"),
243 "always assign an opened file to a variable, and close it afterwards"),
244 (r'[\s\(](open|file)\([^)]*\)\.',
244 (r'[\s\(](open|file)\([^)]*\)\.',
245 "always assign an opened file to a variable, and close it afterwards"),
245 "always assign an opened file to a variable, and close it afterwards"),
246 (r'(?i)descendent', "the proper spelling is descendAnt"),
246 (r'(?i)descendent', "the proper spelling is descendAnt"),
247 (r'\.debug\(\_', "don't mark debug messages for translation"),
247 (r'\.debug\(\_', "don't mark debug messages for translation"),
248 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
248 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
249 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
249 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
250 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
250 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
251 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
251 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
252 "missing _() in ui message (use () to hide false-positives)"),
252 "missing _() in ui message (use () to hide false-positives)"),
253 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
253 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
254 ],
254 ],
255 # warnings
255 # warnings
256 [
256 [
257 ]
257 ]
258 ]
258 ]
259
259
260 pyfilters = [
260 pyfilters = [
261 (r"""(?msx)(?P<comment>\#.*?$)|
261 (r"""(?msx)(?P<comment>\#.*?$)|
262 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
262 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
263 (?P<text>(([^\\]|\\.)*?))
263 (?P<text>(([^\\]|\\.)*?))
264 (?P=quote))""", reppython),
264 (?P=quote))""", reppython),
265 ]
265 ]
266
266
267 txtfilters = []
267 txtfilters = []
268
268
269 txtpats = [
269 txtpats = [
270 [
270 [
271 ('\s$', 'trailing whitespace'),
271 ('\s$', 'trailing whitespace'),
272 ],
272 ],
273 []
273 []
274 ]
274 ]
275
275
276 cpats = [
276 cpats = [
277 [
277 [
278 (r'//', "don't use //-style comments"),
278 (r'//', "don't use //-style comments"),
279 (r'^ ', "don't use spaces to indent"),
279 (r'^ ', "don't use spaces to indent"),
280 (r'\S\t', "don't use tabs except for indent"),
280 (r'\S\t', "don't use tabs except for indent"),
281 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
281 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
282 (r'.{81}', "line too long"),
282 (r'.{81}', "line too long"),
283 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
283 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
284 (r'return\(', "return is not a function"),
284 (r'return\(', "return is not a function"),
285 (r' ;', "no space before ;"),
285 (r' ;', "no space before ;"),
286 (r'\w+\* \w+', "use int *foo, not int* foo"),
286 (r'\w+\* \w+', "use int *foo, not int* foo"),
287 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
287 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
288 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
288 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
289 (r'\w,\w', "missing whitespace after ,"),
289 (r'\w,\w', "missing whitespace after ,"),
290 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
290 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
291 (r'^#\s+\w', "use #foo, not # foo"),
291 (r'^#\s+\w', "use #foo, not # foo"),
292 (r'[^\n]\Z', "no trailing newline"),
292 (r'[^\n]\Z', "no trailing newline"),
293 (r'^\s*#import\b', "use only #include in standard C code"),
293 (r'^\s*#import\b', "use only #include in standard C code"),
294 ],
294 ],
295 # warnings
295 # warnings
296 []
296 []
297 ]
297 ]
298
298
299 cfilters = [
299 cfilters = [
300 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
300 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
301 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
301 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
302 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
302 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
303 (r'(\()([^)]+\))', repcallspaces),
303 (r'(\()([^)]+\))', repcallspaces),
304 ]
304 ]
305
305
306 inutilpats = [
306 inutilpats = [
307 [
307 [
308 (r'\bui\.', "don't use ui in util"),
308 (r'\bui\.', "don't use ui in util"),
309 ],
309 ],
310 # warnings
310 # warnings
311 []
311 []
312 ]
312 ]
313
313
314 inrevlogpats = [
314 inrevlogpats = [
315 [
315 [
316 (r'\brepo\.', "don't use repo in revlog"),
316 (r'\brepo\.', "don't use repo in revlog"),
317 ],
317 ],
318 # warnings
318 # warnings
319 []
319 []
320 ]
320 ]
321
321
322 checks = [
322 checks = [
323 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
323 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
324 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
324 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
325 ('c', r'.*\.c$', cfilters, cpats),
325 ('c', r'.*\.c$', cfilters, cpats),
326 ('unified test', r'.*\.t$', utestfilters, utestpats),
326 ('unified test', r'.*\.t$', utestfilters, utestpats),
327 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
327 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
328 inrevlogpats),
328 inrevlogpats),
329 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
329 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
330 inutilpats),
330 inutilpats),
331 ('txt', r'.*\.txt$', txtfilters, txtpats),
331 ('txt', r'.*\.txt$', txtfilters, txtpats),
332 ]
332 ]
333
333
334 def _preparepats():
334 def _preparepats():
335 for c in checks:
335 for c in checks:
336 failandwarn = c[-1]
336 failandwarn = c[-1]
337 for pats in failandwarn:
337 for pats in failandwarn:
338 for i, pseq in enumerate(pats):
338 for i, pseq in enumerate(pats):
339 # fix-up regexes for multi-line searches
339 # fix-up regexes for multi-line searches
340 p = pseq[0]
340 p = pseq[0]
341 # \s doesn't match \n
341 # \s doesn't match \n
342 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
342 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
343 # [^...] doesn't match newline
343 # [^...] doesn't match newline
344 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
344 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
345
345
346 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
346 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
347 filters = c[2]
347 filters = c[2]
348 for i, flt in enumerate(filters):
348 for i, flt in enumerate(filters):
349 filters[i] = re.compile(flt[0]), flt[1]
349 filters[i] = re.compile(flt[0]), flt[1]
350 _preparepats()
350 _preparepats()
351
351
352 class norepeatlogger(object):
352 class norepeatlogger(object):
353 def __init__(self):
353 def __init__(self):
354 self._lastseen = None
354 self._lastseen = None
355
355
356 def log(self, fname, lineno, line, msg, blame):
356 def log(self, fname, lineno, line, msg, blame):
357 """print error related a to given line of a given file.
357 """print error related a to given line of a given file.
358
358
359 The faulty line will also be printed but only once in the case
359 The faulty line will also be printed but only once in the case
360 of multiple errors.
360 of multiple errors.
361
361
362 :fname: filename
362 :fname: filename
363 :lineno: line number
363 :lineno: line number
364 :line: actual content of the line
364 :line: actual content of the line
365 :msg: error message
365 :msg: error message
366 """
366 """
367 msgid = fname, lineno, line
367 msgid = fname, lineno, line
368 if msgid != self._lastseen:
368 if msgid != self._lastseen:
369 if blame:
369 if blame:
370 print "%s:%d (%s):" % (fname, lineno, blame)
370 print "%s:%d (%s):" % (fname, lineno, blame)
371 else:
371 else:
372 print "%s:%d:" % (fname, lineno)
372 print "%s:%d:" % (fname, lineno)
373 print " > %s" % line
373 print " > %s" % line
374 self._lastseen = msgid
374 self._lastseen = msgid
375 print " " + msg
375 print " " + msg
376
376
377 _defaultlogger = norepeatlogger()
377 _defaultlogger = norepeatlogger()
378
378
379 def getblame(f):
379 def getblame(f):
380 lines = []
380 lines = []
381 for l in os.popen('hg annotate -un %s' % f):
381 for l in os.popen('hg annotate -un %s' % f):
382 start, line = l.split(':', 1)
382 start, line = l.split(':', 1)
383 user, rev = start.split()
383 user, rev = start.split()
384 lines.append((line[1:-1], user, rev))
384 lines.append((line[1:-1], user, rev))
385 return lines
385 return lines
386
386
387 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
387 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
388 blame=False, debug=False, lineno=True):
388 blame=False, debug=False, lineno=True):
389 """checks style and portability of a given file
389 """checks style and portability of a given file
390
390
391 :f: filepath
391 :f: filepath
392 :logfunc: function used to report error
392 :logfunc: function used to report error
393 logfunc(filename, linenumber, linecontent, errormessage)
393 logfunc(filename, linenumber, linecontent, errormessage)
394 :maxerr: number of error to display before aborting.
394 :maxerr: number of error to display before aborting.
395 Set to false (default) to report all errors
395 Set to false (default) to report all errors
396
396
397 return True if no error is found, False otherwise.
397 return True if no error is found, False otherwise.
398 """
398 """
399 blamecache = None
399 blamecache = None
400 result = True
400 result = True
401 for name, match, filters, pats in checks:
401 for name, match, filters, pats in checks:
402 if debug:
402 if debug:
403 print name, f
403 print name, f
404 fc = 0
404 fc = 0
405 if not re.match(match, f):
405 if not re.match(match, f):
406 if debug:
406 if debug:
407 print "Skipping %s for %s it doesn't match %s" % (
407 print "Skipping %s for %s it doesn't match %s" % (
408 name, match, f)
408 name, match, f)
409 continue
409 continue
410 fp = open(f)
410 try:
411 fp = open(f)
412 except IOError, e:
413 print "Skipping %s, %s" % (f, str(e).split(':', 1)[0])
414 continue
411 pre = post = fp.read()
415 pre = post = fp.read()
412 fp.close()
416 fp.close()
413 if "no-" "check-code" in pre:
417 if "no-" "check-code" in pre:
414 if debug:
418 if debug:
415 print "Skipping %s for %s it has no-" " check-code" % (
419 print "Skipping %s for %s it has no-" " check-code" % (
416 name, f)
420 name, f)
417 break
421 break
418 for p, r in filters:
422 for p, r in filters:
419 post = re.sub(p, r, post)
423 post = re.sub(p, r, post)
420 nerrs = len(pats[0]) # nerr elements are errors
424 nerrs = len(pats[0]) # nerr elements are errors
421 if warnings:
425 if warnings:
422 pats = pats[0] + pats[1]
426 pats = pats[0] + pats[1]
423 else:
427 else:
424 pats = pats[0]
428 pats = pats[0]
425 # print post # uncomment to show filtered version
429 # print post # uncomment to show filtered version
426
430
427 if debug:
431 if debug:
428 print "Checking %s for %s" % (name, f)
432 print "Checking %s for %s" % (name, f)
429
433
430 prelines = None
434 prelines = None
431 errors = []
435 errors = []
432 for i, pat in enumerate(pats):
436 for i, pat in enumerate(pats):
433 if len(pat) == 3:
437 if len(pat) == 3:
434 p, msg, ignore = pat
438 p, msg, ignore = pat
435 else:
439 else:
436 p, msg = pat
440 p, msg = pat
437 ignore = None
441 ignore = None
438
442
439 pos = 0
443 pos = 0
440 n = 0
444 n = 0
441 for m in p.finditer(post):
445 for m in p.finditer(post):
442 if prelines is None:
446 if prelines is None:
443 prelines = pre.splitlines()
447 prelines = pre.splitlines()
444 postlines = post.splitlines(True)
448 postlines = post.splitlines(True)
445
449
446 start = m.start()
450 start = m.start()
447 while n < len(postlines):
451 while n < len(postlines):
448 step = len(postlines[n])
452 step = len(postlines[n])
449 if pos + step > start:
453 if pos + step > start:
450 break
454 break
451 pos += step
455 pos += step
452 n += 1
456 n += 1
453 l = prelines[n]
457 l = prelines[n]
454
458
455 if "check-code" "-ignore" in l:
459 if "check-code" "-ignore" in l:
456 if debug:
460 if debug:
457 print "Skipping %s for %s:%s (check-code" "-ignore)" % (
461 print "Skipping %s for %s:%s (check-code" "-ignore)" % (
458 name, f, n)
462 name, f, n)
459 continue
463 continue
460 elif ignore and re.search(ignore, l, re.MULTILINE):
464 elif ignore and re.search(ignore, l, re.MULTILINE):
461 continue
465 continue
462 bd = ""
466 bd = ""
463 if blame:
467 if blame:
464 bd = 'working directory'
468 bd = 'working directory'
465 if not blamecache:
469 if not blamecache:
466 blamecache = getblame(f)
470 blamecache = getblame(f)
467 if n < len(blamecache):
471 if n < len(blamecache):
468 bl, bu, br = blamecache[n]
472 bl, bu, br = blamecache[n]
469 if bl == l:
473 if bl == l:
470 bd = '%s@%s' % (bu, br)
474 bd = '%s@%s' % (bu, br)
471 if i >= nerrs:
475 if i >= nerrs:
472 msg = "warning: " + msg
476 msg = "warning: " + msg
473 errors.append((f, lineno and n + 1, l, msg, bd))
477 errors.append((f, lineno and n + 1, l, msg, bd))
474 result = False
478 result = False
475
479
476 errors.sort()
480 errors.sort()
477 for e in errors:
481 for e in errors:
478 logfunc(*e)
482 logfunc(*e)
479 fc += 1
483 fc += 1
480 if maxerr and fc >= maxerr:
484 if maxerr and fc >= maxerr:
481 print " (too many errors, giving up)"
485 print " (too many errors, giving up)"
482 break
486 break
483
487
484 return result
488 return result
485
489
486 if __name__ == "__main__":
490 if __name__ == "__main__":
487 parser = optparse.OptionParser("%prog [options] [files]")
491 parser = optparse.OptionParser("%prog [options] [files]")
488 parser.add_option("-w", "--warnings", action="store_true",
492 parser.add_option("-w", "--warnings", action="store_true",
489 help="include warning-level checks")
493 help="include warning-level checks")
490 parser.add_option("-p", "--per-file", type="int",
494 parser.add_option("-p", "--per-file", type="int",
491 help="max warnings per file")
495 help="max warnings per file")
492 parser.add_option("-b", "--blame", action="store_true",
496 parser.add_option("-b", "--blame", action="store_true",
493 help="use annotate to generate blame info")
497 help="use annotate to generate blame info")
494 parser.add_option("", "--debug", action="store_true",
498 parser.add_option("", "--debug", action="store_true",
495 help="show debug information")
499 help="show debug information")
496 parser.add_option("", "--nolineno", action="store_false",
500 parser.add_option("", "--nolineno", action="store_false",
497 dest='lineno', help="don't show line numbers")
501 dest='lineno', help="don't show line numbers")
498
502
499 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
503 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
500 lineno=True)
504 lineno=True)
501 (options, args) = parser.parse_args()
505 (options, args) = parser.parse_args()
502
506
503 if len(args) == 0:
507 if len(args) == 0:
504 check = glob.glob("*")
508 check = glob.glob("*")
505 else:
509 else:
506 check = args
510 check = args
507
511
508 ret = 0
512 ret = 0
509 for f in check:
513 for f in check:
510 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
514 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
511 blame=options.blame, debug=options.debug,
515 blame=options.blame, debug=options.debug,
512 lineno=options.lineno):
516 lineno=options.lineno):
513 ret = 1
517 ret = 1
514 sys.exit(ret)
518 sys.exit(ret)
@@ -1,194 +1,196 b''
1 $ cat > correct.py <<EOF
1 $ cat > correct.py <<EOF
2 > def toto(arg1, arg2):
2 > def toto(arg1, arg2):
3 > del arg2
3 > del arg2
4 > return (5 + 6, 9)
4 > return (5 + 6, 9)
5 > EOF
5 > EOF
6 $ cat > wrong.py <<EOF
6 $ cat > wrong.py <<EOF
7 > def toto( arg1, arg2):
7 > def toto( arg1, arg2):
8 > del(arg2)
8 > del(arg2)
9 > return ( 5+6, 9)
9 > return ( 5+6, 9)
10 > EOF
10 > EOF
11 $ cat > quote.py <<EOF
11 $ cat > quote.py <<EOF
12 > # let's use quote in comments
12 > # let's use quote in comments
13 > (''' ( 4x5 )
13 > (''' ( 4x5 )
14 > but """\\''' and finally''',
14 > but """\\''' and finally''',
15 > """let's fool checkpatch""", '1+2',
15 > """let's fool checkpatch""", '1+2',
16 > '"""', 42+1, """and
16 > '"""', 42+1, """and
17 > ( 4-1 ) """, "( 1+1 )\" and ")
17 > ( 4-1 ) """, "( 1+1 )\" and ")
18 > a, '\\\\\\\\', "\\\\\\" x-2", "c-1"
18 > a, '\\\\\\\\', "\\\\\\" x-2", "c-1"
19 > EOF
19 > EOF
20 $ cat > non-py24.py <<EOF
20 $ cat > non-py24.py <<EOF
21 > # Using builtins that does not exist in Python 2.4
21 > # Using builtins that does not exist in Python 2.4
22 > if any():
22 > if any():
23 > x = all()
23 > x = all()
24 > y = format(x)
24 > y = format(x)
25 >
25 >
26 > # Do not complain about our own definition
26 > # Do not complain about our own definition
27 > def any(x):
27 > def any(x):
28 > pass
28 > pass
29 >
29 >
30 > # try/except/finally block does not exist in Python 2.4
30 > # try/except/finally block does not exist in Python 2.4
31 > try:
31 > try:
32 > pass
32 > pass
33 > except StandardError, inst:
33 > except StandardError, inst:
34 > pass
34 > pass
35 > finally:
35 > finally:
36 > pass
36 > pass
37 >
37 >
38 > # nested try/finally+try/except is allowed
38 > # nested try/finally+try/except is allowed
39 > try:
39 > try:
40 > try:
40 > try:
41 > pass
41 > pass
42 > except StandardError, inst:
42 > except StandardError, inst:
43 > pass
43 > pass
44 > finally:
44 > finally:
45 > pass
45 > pass
46 >
46 >
47 > # yield inside a try/finally block is not allowed in Python 2.4
47 > # yield inside a try/finally block is not allowed in Python 2.4
48 > try:
48 > try:
49 > pass
49 > pass
50 > yield 1
50 > yield 1
51 > finally:
51 > finally:
52 > pass
52 > pass
53 > try:
53 > try:
54 > yield
54 > yield
55 > pass
55 > pass
56 > finally:
56 > finally:
57 > pass
57 > pass
58 >
58 >
59 > EOF
59 > EOF
60 $ cat > classstyle.py <<EOF
60 $ cat > classstyle.py <<EOF
61 > class newstyle_class(object):
61 > class newstyle_class(object):
62 > pass
62 > pass
63 >
63 >
64 > class oldstyle_class:
64 > class oldstyle_class:
65 > pass
65 > pass
66 >
66 >
67 > class empty():
67 > class empty():
68 > pass
68 > pass
69 >
69 >
70 > no_class = 1:
70 > no_class = 1:
71 > pass
71 > pass
72 > EOF
72 > EOF
73 $ check_code="$TESTDIR"/../contrib/check-code.py
73 $ check_code="$TESTDIR"/../contrib/check-code.py
74 $ "$check_code" ./wrong.py ./correct.py ./quote.py ./non-py24.py ./classstyle.py
74 $ "$check_code" ./wrong.py ./correct.py ./quote.py ./non-py24.py ./classstyle.py
75 ./wrong.py:1:
75 ./wrong.py:1:
76 > def toto( arg1, arg2):
76 > def toto( arg1, arg2):
77 gratuitous whitespace in () or []
77 gratuitous whitespace in () or []
78 ./wrong.py:2:
78 ./wrong.py:2:
79 > del(arg2)
79 > del(arg2)
80 Python keyword is not a function
80 Python keyword is not a function
81 ./wrong.py:3:
81 ./wrong.py:3:
82 > return ( 5+6, 9)
82 > return ( 5+6, 9)
83 gratuitous whitespace in () or []
83 gratuitous whitespace in () or []
84 missing whitespace in expression
84 missing whitespace in expression
85 ./quote.py:5:
85 ./quote.py:5:
86 > '"""', 42+1, """and
86 > '"""', 42+1, """and
87 missing whitespace in expression
87 missing whitespace in expression
88 ./non-py24.py:2:
88 ./non-py24.py:2:
89 > if any():
89 > if any():
90 any/all/format not available in Python 2.4
90 any/all/format not available in Python 2.4
91 ./non-py24.py:3:
91 ./non-py24.py:3:
92 > x = all()
92 > x = all()
93 any/all/format not available in Python 2.4
93 any/all/format not available in Python 2.4
94 ./non-py24.py:4:
94 ./non-py24.py:4:
95 > y = format(x)
95 > y = format(x)
96 any/all/format not available in Python 2.4
96 any/all/format not available in Python 2.4
97 ./non-py24.py:11:
97 ./non-py24.py:11:
98 > try:
98 > try:
99 no try/except/finally in Python 2.4
99 no try/except/finally in Python 2.4
100 ./non-py24.py:28:
100 ./non-py24.py:28:
101 > try:
101 > try:
102 no yield inside try/finally in Python 2.4
102 no yield inside try/finally in Python 2.4
103 ./non-py24.py:33:
103 ./non-py24.py:33:
104 > try:
104 > try:
105 no yield inside try/finally in Python 2.4
105 no yield inside try/finally in Python 2.4
106 ./classstyle.py:4:
106 ./classstyle.py:4:
107 > class oldstyle_class:
107 > class oldstyle_class:
108 old-style class, use class foo(object)
108 old-style class, use class foo(object)
109 ./classstyle.py:7:
109 ./classstyle.py:7:
110 > class empty():
110 > class empty():
111 class foo() not available in Python 2.4, use class foo(object)
111 class foo() not available in Python 2.4, use class foo(object)
112 [1]
112 [1]
113 $ cat > python3-compat.py << EOF
113 $ cat > python3-compat.py << EOF
114 > foo <> bar
114 > foo <> bar
115 > reduce(lambda a, b: a + b, [1, 2, 3, 4])
115 > reduce(lambda a, b: a + b, [1, 2, 3, 4])
116 > EOF
116 > EOF
117 $ "$check_code" python3-compat.py
117 $ "$check_code" python3-compat.py
118 python3-compat.py:1:
118 python3-compat.py:1:
119 > foo <> bar
119 > foo <> bar
120 <> operator is not available in Python 3+, use !=
120 <> operator is not available in Python 3+, use !=
121 python3-compat.py:2:
121 python3-compat.py:2:
122 > reduce(lambda a, b: a + b, [1, 2, 3, 4])
122 > reduce(lambda a, b: a + b, [1, 2, 3, 4])
123 reduce is not available in Python 3+
123 reduce is not available in Python 3+
124 [1]
124 [1]
125
125
126 $ cat > is-op.py <<EOF
126 $ cat > is-op.py <<EOF
127 > # is-operator comparing number or string literal
127 > # is-operator comparing number or string literal
128 > x = None
128 > x = None
129 > y = x is 'foo'
129 > y = x is 'foo'
130 > y = x is "foo"
130 > y = x is "foo"
131 > y = x is 5346
131 > y = x is 5346
132 > y = x is -6
132 > y = x is -6
133 > y = x is not 'foo'
133 > y = x is not 'foo'
134 > y = x is not "foo"
134 > y = x is not "foo"
135 > y = x is not 5346
135 > y = x is not 5346
136 > y = x is not -6
136 > y = x is not -6
137 > EOF
137 > EOF
138
138
139 $ "$check_code" ./is-op.py
139 $ "$check_code" ./is-op.py
140 ./is-op.py:3:
140 ./is-op.py:3:
141 > y = x is 'foo'
141 > y = x is 'foo'
142 object comparison with literal
142 object comparison with literal
143 ./is-op.py:4:
143 ./is-op.py:4:
144 > y = x is "foo"
144 > y = x is "foo"
145 object comparison with literal
145 object comparison with literal
146 ./is-op.py:5:
146 ./is-op.py:5:
147 > y = x is 5346
147 > y = x is 5346
148 object comparison with literal
148 object comparison with literal
149 ./is-op.py:6:
149 ./is-op.py:6:
150 > y = x is -6
150 > y = x is -6
151 object comparison with literal
151 object comparison with literal
152 ./is-op.py:7:
152 ./is-op.py:7:
153 > y = x is not 'foo'
153 > y = x is not 'foo'
154 object comparison with literal
154 object comparison with literal
155 ./is-op.py:8:
155 ./is-op.py:8:
156 > y = x is not "foo"
156 > y = x is not "foo"
157 object comparison with literal
157 object comparison with literal
158 ./is-op.py:9:
158 ./is-op.py:9:
159 > y = x is not 5346
159 > y = x is not 5346
160 object comparison with literal
160 object comparison with literal
161 ./is-op.py:10:
161 ./is-op.py:10:
162 > y = x is not -6
162 > y = x is not -6
163 object comparison with literal
163 object comparison with literal
164 [1]
164 [1]
165
165
166 $ cat > for-nolineno.py <<EOF
166 $ cat > for-nolineno.py <<EOF
167 > except:
167 > except:
168 > EOF
168 > EOF
169 $ "$check_code" for-nolineno.py --nolineno
169 $ "$check_code" for-nolineno.py --nolineno
170 for-nolineno.py:0:
170 for-nolineno.py:0:
171 > except:
171 > except:
172 naked except clause
172 naked except clause
173 [1]
173 [1]
174
174
175 $ cat > warning.t <<EOF
175 $ cat > warning.t <<EOF
176 > $ function warnonly {
176 > $ function warnonly {
177 > > }
177 > > }
178 > EOF
178 > EOF
179 $ "$check_code" warning.t
179 $ "$check_code" warning.t
180 $ "$check_code" --warn warning.t
180 $ "$check_code" --warn warning.t
181 warning.t:1:
181 warning.t:1:
182 > $ function warnonly {
182 > $ function warnonly {
183 warning: don't use 'function', use old style
183 warning: don't use 'function', use old style
184 [1]
184 [1]
185 $ cat > raise-format.py <<EOF
185 $ cat > raise-format.py <<EOF
186 > raise SomeException, message
186 > raise SomeException, message
187 > # this next line is okay
187 > # this next line is okay
188 > raise SomeException(arg1, arg2)
188 > raise SomeException(arg1, arg2)
189 > EOF
189 > EOF
190 $ "$check_code" raise-format.py
190 $ "$check_code" not-existing.py raise-format.py
191 Skipping*not-existing.py* (glob)
191 raise-format.py:1:
192 raise-format.py:1:
192 > raise SomeException, message
193 > raise SomeException, message
193 don't use old-style two-argument raise, use Exception(message)
194 don't use old-style two-argument raise, use Exception(message)
194 [1]
195 [1]
196
General Comments 0
You need to be logged in to leave comments. Login now