Mercurial > public > mercurial-scm > hg
annotate contrib/check-code.py @ 12866:eddc20306ab6 stable
encoding: default ambiguous character to narrow
The current implementation of colwidth was treating 'A'mbiguous
characters as wide, which was incorrect in a non-East Asian context.
As per http://unicode.org/reports/tr11/#Recommendations, we should
instead default to 'narrow' if we don't know better. As character
width is dependent on the particular font used and we have no idea
what fonts are in use, this recommendation applies.
This introduces HGENCODINGAMBIGUOUS to get the old behavior back.
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Wed, 27 Oct 2010 15:35:21 -0500 |
parents | c7d23b4ca4ba |
children | 34034e55424f |
rev | line source |
---|---|
10281 | 1 #!/usr/bin/env python |
2 # | |
3 # check-code - a style and portability checker for Mercurial | |
4 # | |
10290
7cc60de189d7
check-code: fix copyright date
Matt Mackall <mpm@selenic.com>
parents:
10287
diff
changeset
|
5 # Copyright 2010 Matt Mackall <mpm@selenic.com> |
10281 | 6 # |
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. | |
9 | |
11816
e1359ad582f6
check-code: add exit status
Alecs King <alecsk@gmail.com>
parents:
11764
diff
changeset
|
10 import re, glob, os, sys |
10895
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
11 import optparse |
10281 | 12 |
13 def repquote(m): | |
10722
c4fb2103e734
check-code: improve quote detection regexp, add tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10720
diff
changeset
|
14 t = re.sub(r"\w", "x", m.group('text')) |
10451
63a9bfad50ff
check-code: two more rules
Matt Mackall <mpm@selenic.com>
parents:
10412
diff
changeset
|
15 t = re.sub(r"[^\sx]", "o", t) |
10722
c4fb2103e734
check-code: improve quote detection regexp, add tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10720
diff
changeset
|
16 return m.group('quote') + t + m.group('quote') |
10281 | 17 |
10727
62b8f15683f2
check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10723
diff
changeset
|
18 def reppython(m): |
62b8f15683f2
check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10723
diff
changeset
|
19 comment = m.group('comment') |
62b8f15683f2
check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10723
diff
changeset
|
20 if comment: |
62b8f15683f2
check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10723
diff
changeset
|
21 return "#" * len(comment) |
62b8f15683f2
check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10723
diff
changeset
|
22 return repquote(m) |
10281 | 23 |
24 def repcomment(m): | |
25 return m.group(1) + "#" * len(m.group(2)) | |
26 | |
27 def repccomment(m): | |
28 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2)) | |
29 return m.group(1) + t + "*/" | |
30 | |
31 def repcallspaces(m): | |
32 t = re.sub(r"\n\s+", "\n", m.group(2)) | |
33 return m.group(1) + t | |
34 | |
35 def repinclude(m): | |
36 return m.group(1) + "<foo>" | |
37 | |
38 def rephere(m): | |
39 t = re.sub(r"\S", "x", m.group(2)) | |
40 return m.group(1) + t | |
41 | |
42 | |
43 testpats = [ | |
10374
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
44 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"), |
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
45 (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"), |
10281 | 46 (r'^function', "don't use 'function', use old style"), |
10374
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
47 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"), |
10373
e4c7972002e4
check-code.py: escape backslash
Mads Kiilerich <mads@kiilerich.com>
parents:
10291
diff
changeset
|
48 (r'echo.*\\n', "don't use 'echo \\n', use printf"), |
11884
932448701e7d
check-code: catch "echo -n" in tests
Martin Geisler <mg@lazybytes.net>
parents:
11599
diff
changeset
|
49 (r'echo -n', "don't use 'echo -n', use printf"), |
10374
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
50 (r'^diff.*-\w*N', "don't use 'diff -N'"), |
10281 | 51 (r'(^| )wc[^|]*$', "filter wc output"), |
10374
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
52 (r'head -c', "don't use 'head -c', use 'dd'"), |
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
53 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"), |
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
54 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"), |
3aa35db5e38c
check-code.py: make help strings consistent
Martin Geisler <mg@lazybytes.net>
parents:
10373
diff
changeset
|
55 (r'printf.*\\x', "don't use printf \\x, use Python"), |
10281 | 56 (r'\$\(.*\)', "don't use $(expr), use `expr`"), |
57 (r'rm -rf \*', "don't use naked rm -rf, target a directory"), | |
58 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w', | |
59 "use egrep for extended grep syntax"), | |
60 (r'/bin/', "don't use explicit paths for tools"), | |
61 (r'\$PWD', "don't use $PWD, use `pwd`"), | |
62 (r'[^\n]\Z', "no trailing newline"), | |
10658
95c7c4b7e67a
test-merge-default and check-code.py: No "export x=x" in sh
Mads Kiilerich <mads@kiilerich.com>
parents:
10451
diff
changeset
|
63 (r'export.*=', "don't export and assign at once"), |
10802
6e4cf8319f54
check-code.py: Check for bare ^
Mads Kiilerich <mads@kiilerich.com>
parents:
10658
diff
changeset
|
64 ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"), |
11210
0c0088881562
check-code: add check for 'source'
Yuya Nishihara <yuya@tcha.org>
parents:
10905
diff
changeset
|
65 (r'^source\b', "don't use 'source', use '.'"), |
12367
3acd5f7ab9d0
tests: compatibility fix.
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents:
12366
diff
changeset
|
66 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"), |
10281 | 67 ] |
68 | |
69 testfilters = [ | |
70 (r"( *)(#([^\n]*\S)?)", repcomment), | |
71 (r"<<(\S+)((.|\n)*?\n\1)", rephere), | |
72 ] | |
73 | |
12364
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
74 uprefix = r"^ \$ " |
12743
4c4aeaab2339
check-code: add 'no tab indent' check for unified tests
Adrian Buehlmann <adrian@cadifra.com>
parents:
12367
diff
changeset
|
75 uprefixc = r"^ > " |
12364
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
76 utestpats = [ |
12785
c7d23b4ca4ba
check-code: warning and fixes for whitespace in unified tests
Matt Mackall <mpm@selenic.com>
parents:
12770
diff
changeset
|
77 (r'^(\S| $ ).*(\S\s+|^\s+)\n', "trailing whitespace on non-output"), |
12366
c01dc9087d9a
tests: drop a bunch of sed calls from unified tests
Matt Mackall <mpm@selenic.com>
parents:
12364
diff
changeset
|
78 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"), |
12364
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
79 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"), |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
80 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"), |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
81 (uprefix + r'.*\|\| echo.*(fail|error)', |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
82 "explicit exit code checks unnecessary"), |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
83 (uprefix + r'set -e', "don't use set -e"), |
12743
4c4aeaab2339
check-code: add 'no tab indent' check for unified tests
Adrian Buehlmann <adrian@cadifra.com>
parents:
12367
diff
changeset
|
84 (uprefixc + r'( *)\t', "don't use tabs to indent"), |
12364
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
85 ] |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
86 |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
87 for p, m in testpats: |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
88 if p.startswith('^'): |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
89 p = uprefix + p[1:] |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
90 else: |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
91 p = uprefix + p |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
92 utestpats.append((p, m)) |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
93 |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
94 utestfilters = [ |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
95 (r"( *)(#([^\n]*\S)?)", repcomment), |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
96 ] |
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
97 |
10281 | 98 pypats = [ |
11568
d5d4e6a30613
check-code: check for tuple parameter unpacking (missing in py3k)
Renato Cunha <renatoc@gmail.com>
parents:
11522
diff
changeset
|
99 (r'^\s*def\s*\w+\s*\(.*,\s*\(', |
d5d4e6a30613
check-code: check for tuple parameter unpacking (missing in py3k)
Renato Cunha <renatoc@gmail.com>
parents:
11522
diff
changeset
|
100 "tuple parameter unpacking not available in Python 3+"), |
d5d4e6a30613
check-code: check for tuple parameter unpacking (missing in py3k)
Renato Cunha <renatoc@gmail.com>
parents:
11522
diff
changeset
|
101 (r'lambda\s*\(.*,.*\)', |
d5d4e6a30613
check-code: check for tuple parameter unpacking (missing in py3k)
Renato Cunha <renatoc@gmail.com>
parents:
11522
diff
changeset
|
102 "tuple parameter unpacking not available in Python 3+"), |
11764
16723af520b0
check-code: added a check for calls to the builtin cmp function
Renato Cunha <renatoc@gmail.com>
parents:
11672
diff
changeset
|
103 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"), |
11569
f8576644a222
check-code: added check for reduce usage
Renato Cunha <renatoc@gmail.com>
parents:
11568
diff
changeset
|
104 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"), |
11602
ba2520dd1e29
check-code: catch dict.has_key
Martin Geisler <mg@lazybytes.net>
parents:
11601
diff
changeset
|
105 (r'\.has_key\b', "dict.has_key is not available in Python 3+"), |
10281 | 106 (r'^\s*\t', "don't use tabs"), |
10412
5326800d6937
check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents:
10374
diff
changeset
|
107 (r'\S;\s*\n', "semicolon"), |
10281 | 108 (r'\w,\w', "missing whitespace after ,"), |
109 (r'\w[+/*\-<>]\w', "missing whitespace in expression"), | |
110 (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"), | |
111 (r'.{85}', "line too long"), | |
11672
dad185761392
check-code: add warning on lines over 80 characters
Matt Mackall <mpm@selenic.com>
parents:
11604
diff
changeset
|
112 (r'.{81}', "warning: line over 80 characters"), |
10281 | 113 (r'[^\n]\Z', "no trailing newline"), |
12770
614f0d8724ab
check-code: find trailing whitespace
Martin Geisler <mg@lazybytes.net>
parents:
12743
diff
changeset
|
114 (r'(\S\s+|^\s+)\n', "trailing whitespace"), |
10281 | 115 # (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"), |
116 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"), | |
10286 | 117 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+', |
118 "linebreak after :"), | |
10281 | 119 (r'class\s[^(]:', "old-style class, use class foo(object)"), |
10291
61c93743fae0
check-code: del isn't a function
Matt Mackall <mpm@selenic.com>
parents:
10290
diff
changeset
|
120 (r'^\s+del\(', "del isn't a function"), |
10281 | 121 (r'^\s+except\(', "except isn't a function"), |
10412
5326800d6937
check-code: import some pylint checks
Matt Mackall <mpm@selenic.com>
parents:
10374
diff
changeset
|
122 (r',]', "unneeded trailing ',' in list"), |
10281 | 123 # (r'class\s[A-Z][^\(]*\((?!Exception)', |
124 # "don't capitalize non-exception classes"), | |
125 # (r'in range\(', "use xrange"), | |
126 # (r'^\s*print\s+', "avoid using print in core and extensions"), | |
127 (r'[\x80-\xff]', "non-ASCII character literal"), | |
128 (r'("\')\.format\(', "str.format() not available in Python 2.4"), | |
129 (r'^\s*with\s+', "with not available in Python 2.4"), | |
11345
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
130 (r'(?<!def)\s+(any|all|format)\(', |
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
131 "any/all/format not available in Python 2.4"), |
11522
eaa7666ad53f
check-code: add test for callable
Martin Geisler <mg@aragost.com>
parents:
11345
diff
changeset
|
132 (r'(?<!def)\s+(callable)\(', |
eaa7666ad53f
check-code: add test for callable
Martin Geisler <mg@aragost.com>
parents:
11345
diff
changeset
|
133 "callable not available in Python 3, use hasattr(f, '__call__')"), |
10281 | 134 (r'if\s.*\selse', "if ... else form not available in Python 2.4"), |
135 (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"), | |
136 # (r'\s\s=', "gratuitous whitespace before ="), | |
11345
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
137 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S', |
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
138 "missing whitespace around operator"), |
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
139 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s', |
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
140 "missing whitespace around operator"), |
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
141 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S', |
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
142 "missing whitespace around operator"), |
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
143 (r'[^+=*!<>&| -](\s=|=\s)[^= ]', |
4b81f82b03e3
check-code: reformat long lines
Martin Geisler <mg@aragost.com>
parents:
11343
diff
changeset
|
144 "wrong whitespace around ="), |
10451
63a9bfad50ff
check-code: two more rules
Matt Mackall <mpm@selenic.com>
parents:
10412
diff
changeset
|
145 (r'raise Exception', "don't raise generic exceptions"), |
11599
6fcc066c0c2c
check-code: warn about untranslated ui.warn calls
Martin Geisler <mg@lazybytes.net>
parents:
11522
diff
changeset
|
146 (r'ui\.(status|progress|write|note|warn)\([\'\"]x', |
10895
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
147 "warning: unwrapped ui message"), |
10281 | 148 ] |
149 | |
150 pyfilters = [ | |
10727
62b8f15683f2
check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10723
diff
changeset
|
151 (r"""(?msx)(?P<comment>\#.*?$)| |
62b8f15683f2
check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10723
diff
changeset
|
152 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!"))) |
62b8f15683f2
check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10723
diff
changeset
|
153 (?P<text>(([^\\]|\\.)*?)) |
62b8f15683f2
check-code: more tests and more robust python filtering
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10723
diff
changeset
|
154 (?P=quote))""", reppython), |
10281 | 155 ] |
156 | |
157 cpats = [ | |
158 (r'//', "don't use //-style comments"), | |
159 (r'^ ', "don't use spaces to indent"), | |
160 (r'\S\t', "don't use tabs except for indent"), | |
161 (r'(\S\s+|^\s+)\n', "trailing whitespace"), | |
162 (r'.{85}', "line too long"), | |
163 (r'(while|if|do|for)\(', "use space after while/if/do/for"), | |
164 (r'return\(', "return is not a function"), | |
165 (r' ;', "no space before ;"), | |
166 (r'\w+\* \w+', "use int *foo, not int* foo"), | |
167 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"), | |
168 (r'\S+ (\+\+|--)', "use foo++, not foo ++"), | |
169 (r'\w,\w', "missing whitespace after ,"), | |
170 (r'\w[+/*]\w', "missing whitespace in expression"), | |
171 (r'^#\s+\w', "use #foo, not # foo"), | |
172 (r'[^\n]\Z', "no trailing newline"), | |
173 ] | |
174 | |
175 cfilters = [ | |
176 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment), | |
10722
c4fb2103e734
check-code: improve quote detection regexp, add tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10720
diff
changeset
|
177 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote), |
10281 | 178 (r'''(#\s*include\s+<)([^>]+)>''', repinclude), |
179 (r'(\()([^)]+\))', repcallspaces), | |
180 ] | |
181 | |
182 checks = [ | |
183 ('python', r'.*\.(py|cgi)$', pyfilters, pypats), | |
184 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats), | |
185 ('c', r'.*\.c$', cfilters, cpats), | |
12364
e128fa4615f2
check-code: add some basic support for unified tests
Matt Mackall <mpm@selenic.com>
parents:
11886
diff
changeset
|
186 ('unified test', r'.*\.t$', utestfilters, utestpats), |
10281 | 187 ] |
188 | |
10719
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
189 class norepeatlogger(object): |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
190 def __init__(self): |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
191 self._lastseen = None |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
192 |
11604
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
193 def log(self, fname, lineno, line, msg, blame): |
10719
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
194 """print error related a to given line of a given file. |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
195 |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
196 The faulty line will also be printed but only once in the case |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
197 of multiple errors. |
10281 | 198 |
10719
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
199 :fname: filename |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
200 :lineno: line number |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
201 :line: actual content of the line |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
202 :msg: error message |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
203 """ |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
204 msgid = fname, lineno, line |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
205 if msgid != self._lastseen: |
11604
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
206 if blame: |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
207 print "%s:%d (%s):" % (fname, lineno, blame) |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
208 else: |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
209 print "%s:%d:" % (fname, lineno) |
10719
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
210 print " > %s" % line |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
211 self._lastseen = msgid |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
212 print " " + msg |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
213 |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
214 _defaultlogger = norepeatlogger() |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
215 |
11604
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
216 def getblame(f): |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
217 lines = [] |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
218 for l in os.popen('hg annotate -un %s' % f): |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
219 start, line = l.split(':', 1) |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
220 user, rev = start.split() |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
221 lines.append((line[1:-1], user, rev)) |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
222 return lines |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
223 |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
224 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False, |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
225 blame=False): |
10719
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
226 """checks style and portability of a given file |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
227 |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
228 :f: filepath |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
229 :logfunc: function used to report error |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
230 logfunc(filename, linenumber, linecontent, errormessage) |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
231 :maxerr: number of error to display before arborting. |
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
232 Set to None (default) to report all errors |
10720
fbcccf9ec58f
check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10719
diff
changeset
|
233 |
fbcccf9ec58f
check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10719
diff
changeset
|
234 return True if no error is found, False otherwise. |
10719
3be9ae49b628
code-code: Add a logfunc argument to checkfile
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10718
diff
changeset
|
235 """ |
11604
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
236 blamecache = None |
10720
fbcccf9ec58f
check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10719
diff
changeset
|
237 result = True |
10281 | 238 for name, match, filters, pats in checks: |
239 fc = 0 | |
240 if not re.match(match, f): | |
241 continue | |
242 pre = post = open(f).read() | |
10287
5da892be3497
check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents:
10286
diff
changeset
|
243 if "no-" + "check-code" in pre: |
5da892be3497
check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents:
10286
diff
changeset
|
244 break |
10281 | 245 for p, r in filters: |
246 post = re.sub(p, r, post) | |
247 # print post # uncomment to show filtered version | |
248 z = enumerate(zip(pre.splitlines(), post.splitlines(True))) | |
249 for n, l in z: | |
10287
5da892be3497
check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents:
10286
diff
changeset
|
250 if "check-code" + "-ignore" in l[0]: |
5da892be3497
check-code: add some ignore hints
Matt Mackall <mpm@selenic.com>
parents:
10286
diff
changeset
|
251 continue |
10281 | 252 for p, msg in pats: |
10895
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
253 if not warnings and msg.startswith("warning"): |
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
254 continue |
10281 | 255 if re.search(p, l[1]): |
11604
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
256 bd = "" |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
257 if blame: |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
258 bd = 'working directory' |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
259 if not blamecache: |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
260 blamecache = getblame(f) |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
261 if n < len(blamecache): |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
262 bl, bu, br = blamecache[n] |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
263 if bl == l[0]: |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
264 bd = '%s@%s' % (bu, br) |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
265 logfunc(f, n + 1, l[0], msg, bd) |
10281 | 266 fc += 1 |
10720
fbcccf9ec58f
check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10719
diff
changeset
|
267 result = False |
10718
f18c37fd624f
check-code: Add a ``maxerr`` argument to the ``checkfile`` function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10717
diff
changeset
|
268 if maxerr is not None and fc >= maxerr: |
10281 | 269 print " (too many errors, giving up)" |
270 break | |
271 break | |
10720
fbcccf9ec58f
check-code: add a return value to checkfile function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10719
diff
changeset
|
272 return result |
10717
b1f4fcef99b3
check-code: Add a ``checkfile`` function
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10716
diff
changeset
|
273 |
10716
5f92bde72eef
check-code: Only call check-code if __name__ = "__main__".
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10707
diff
changeset
|
274 if __name__ == "__main__": |
10895
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
275 parser = optparse.OptionParser("%prog [options] [files]") |
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
276 parser.add_option("-w", "--warnings", action="store_true", |
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
277 help="include warning-level checks") |
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
278 parser.add_option("-p", "--per-file", type="int", |
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
279 help="max warnings per file") |
11604
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
280 parser.add_option("-b", "--blame", action="store_true", |
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
281 help="use annotate to generate blame info") |
10895
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
282 |
11604
c5d40818b270
check-code: add --blame switch
Matt Mackall <mpm@selenic.com>
parents:
11602
diff
changeset
|
283 parser.set_defaults(per_file=15, warnings=False, blame=False) |
10895
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
284 (options, args) = parser.parse_args() |
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
285 |
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
286 if len(args) == 0: |
10716
5f92bde72eef
check-code: Only call check-code if __name__ = "__main__".
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10707
diff
changeset
|
287 check = glob.glob("*") |
5f92bde72eef
check-code: Only call check-code if __name__ = "__main__".
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10707
diff
changeset
|
288 else: |
10895
217557b26bc7
check-code: add a warnings level
Matt Mackall <mpm@selenic.com>
parents:
10814
diff
changeset
|
289 check = args |
10281 | 290 |
10716
5f92bde72eef
check-code: Only call check-code if __name__ = "__main__".
Pierre-Yves David <pierre-yves.david@logilab.fr>
parents:
10707
diff
changeset
|
291 for f in check: |
11816
e1359ad582f6
check-code: add exit status
Alecs King <alecsk@gmail.com>
parents:
11764
diff
changeset
|
292 ret = 0 |
e1359ad582f6
check-code: add exit status
Alecs King <alecsk@gmail.com>
parents:
11764
diff
changeset
|
293 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings, |
e1359ad582f6
check-code: add exit status
Alecs King <alecsk@gmail.com>
parents:
11764
diff
changeset
|
294 blame=options.blame): |
e1359ad582f6
check-code: add exit status
Alecs King <alecsk@gmail.com>
parents:
11764
diff
changeset
|
295 ret = 1 |
e1359ad582f6
check-code: add exit status
Alecs King <alecsk@gmail.com>
parents:
11764
diff
changeset
|
296 sys.exit(ret) |