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