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