##// END OF EJS Templates
check-config: specify the mode 'rb' to open the file...
Pulkit Goyal -
r35937:143d7b27 default
parent child Browse files
Show More
@@ -1,142 +1,142 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # check-config - a config flag documentation checker for Mercurial
3 # check-config - a config flag documentation checker for Mercurial
4 #
4 #
5 # Copyright 2015 Matt Mackall <mpm@selenic.com>
5 # Copyright 2015 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 from __future__ import absolute_import, print_function
10 from __future__ import absolute_import, print_function
11 import re
11 import re
12 import sys
12 import sys
13
13
14 foundopts = {}
14 foundopts = {}
15 documented = {}
15 documented = {}
16 allowinconsistent = set()
16 allowinconsistent = set()
17
17
18 configre = re.compile(r'''
18 configre = re.compile(r'''
19 # Function call
19 # Function call
20 ui\.config(?P<ctype>|int|bool|list)\(
20 ui\.config(?P<ctype>|int|bool|list)\(
21 # First argument.
21 # First argument.
22 ['"](?P<section>\S+)['"],\s*
22 ['"](?P<section>\S+)['"],\s*
23 # Second argument
23 # Second argument
24 ['"](?P<option>\S+)['"](,\s+
24 ['"](?P<option>\S+)['"](,\s+
25 (?:default=)?(?P<default>\S+?))?
25 (?:default=)?(?P<default>\S+?))?
26 \)''', re.VERBOSE | re.MULTILINE)
26 \)''', re.VERBOSE | re.MULTILINE)
27
27
28 configwithre = re.compile('''
28 configwithre = re.compile('''
29 ui\.config(?P<ctype>with)\(
29 ui\.config(?P<ctype>with)\(
30 # First argument is callback function. This doesn't parse robustly
30 # First argument is callback function. This doesn't parse robustly
31 # if it is e.g. a function call.
31 # if it is e.g. a function call.
32 [^,]+,\s*
32 [^,]+,\s*
33 ['"](?P<section>\S+)['"],\s*
33 ['"](?P<section>\S+)['"],\s*
34 ['"](?P<option>\S+)['"](,\s+
34 ['"](?P<option>\S+)['"](,\s+
35 (?:default=)?(?P<default>\S+?))?
35 (?:default=)?(?P<default>\S+?))?
36 \)''', re.VERBOSE | re.MULTILINE)
36 \)''', re.VERBOSE | re.MULTILINE)
37
37
38 configpartialre = (r"""ui\.config""")
38 configpartialre = (r"""ui\.config""")
39
39
40 ignorere = re.compile(r'''
40 ignorere = re.compile(r'''
41 \#\s(?P<reason>internal|experimental|deprecated|developer|inconsistent)\s
41 \#\s(?P<reason>internal|experimental|deprecated|developer|inconsistent)\s
42 config:\s(?P<config>\S+\.\S+)$
42 config:\s(?P<config>\S+\.\S+)$
43 ''', re.VERBOSE | re.MULTILINE)
43 ''', re.VERBOSE | re.MULTILINE)
44
44
45 def main(args):
45 def main(args):
46 for f in args:
46 for f in args:
47 sect = ''
47 sect = ''
48 prevname = ''
48 prevname = ''
49 confsect = ''
49 confsect = ''
50 carryover = ''
50 carryover = ''
51 linenum = 0
51 linenum = 0
52 for l in open(f):
52 for l in open(f, 'rb'):
53 linenum += 1
53 linenum += 1
54
54
55 # check topic-like bits
55 # check topic-like bits
56 m = re.match('\s*``(\S+)``', l)
56 m = re.match('\s*``(\S+)``', l)
57 if m:
57 if m:
58 prevname = m.group(1)
58 prevname = m.group(1)
59 if re.match('^\s*-+$', l):
59 if re.match('^\s*-+$', l):
60 sect = prevname
60 sect = prevname
61 prevname = ''
61 prevname = ''
62
62
63 if sect and prevname:
63 if sect and prevname:
64 name = sect + '.' + prevname
64 name = sect + '.' + prevname
65 documented[name] = 1
65 documented[name] = 1
66
66
67 # check docstring bits
67 # check docstring bits
68 m = re.match(r'^\s+\[(\S+)\]', l)
68 m = re.match(r'^\s+\[(\S+)\]', l)
69 if m:
69 if m:
70 confsect = m.group(1)
70 confsect = m.group(1)
71 continue
71 continue
72 m = re.match(r'^\s+(?:#\s*)?(\S+) = ', l)
72 m = re.match(r'^\s+(?:#\s*)?(\S+) = ', l)
73 if m:
73 if m:
74 name = confsect + '.' + m.group(1)
74 name = confsect + '.' + m.group(1)
75 documented[name] = 1
75 documented[name] = 1
76
76
77 # like the bugzilla extension
77 # like the bugzilla extension
78 m = re.match(r'^\s*(\S+\.\S+)$', l)
78 m = re.match(r'^\s*(\S+\.\S+)$', l)
79 if m:
79 if m:
80 documented[m.group(1)] = 1
80 documented[m.group(1)] = 1
81
81
82 # like convert
82 # like convert
83 m = re.match(r'^\s*:(\S+\.\S+):\s+', l)
83 m = re.match(r'^\s*:(\S+\.\S+):\s+', l)
84 if m:
84 if m:
85 documented[m.group(1)] = 1
85 documented[m.group(1)] = 1
86
86
87 # quoted in help or docstrings
87 # quoted in help or docstrings
88 m = re.match(r'.*?``(\S+\.\S+)``', l)
88 m = re.match(r'.*?``(\S+\.\S+)``', l)
89 if m:
89 if m:
90 documented[m.group(1)] = 1
90 documented[m.group(1)] = 1
91
91
92 # look for ignore markers
92 # look for ignore markers
93 m = ignorere.search(l)
93 m = ignorere.search(l)
94 if m:
94 if m:
95 if m.group('reason') == 'inconsistent':
95 if m.group('reason') == 'inconsistent':
96 allowinconsistent.add(m.group('config'))
96 allowinconsistent.add(m.group('config'))
97 else:
97 else:
98 documented[m.group('config')] = 1
98 documented[m.group('config')] = 1
99
99
100 # look for code-like bits
100 # look for code-like bits
101 line = carryover + l
101 line = carryover + l
102 m = configre.search(line) or configwithre.search(line)
102 m = configre.search(line) or configwithre.search(line)
103 if m:
103 if m:
104 ctype = m.group('ctype')
104 ctype = m.group('ctype')
105 if not ctype:
105 if not ctype:
106 ctype = 'str'
106 ctype = 'str'
107 name = m.group('section') + "." + m.group('option')
107 name = m.group('section') + "." + m.group('option')
108 default = m.group('default')
108 default = m.group('default')
109 if default in (None, 'False', 'None', '0', '[]', '""', "''"):
109 if default in (None, 'False', 'None', '0', '[]', '""', "''"):
110 default = ''
110 default = ''
111 if re.match('[a-z.]+$', default):
111 if re.match('[a-z.]+$', default):
112 default = '<variable>'
112 default = '<variable>'
113 if (name in foundopts and (ctype, default) != foundopts[name]
113 if (name in foundopts and (ctype, default) != foundopts[name]
114 and name not in allowinconsistent):
114 and name not in allowinconsistent):
115 print(l.rstrip())
115 print(l.rstrip())
116 print("conflict on %s: %r != %r" % (name, (ctype, default),
116 print("conflict on %s: %r != %r" % (name, (ctype, default),
117 foundopts[name]))
117 foundopts[name]))
118 print("at %s:%d:" % (f, linenum))
118 print("at %s:%d:" % (f, linenum))
119 foundopts[name] = (ctype, default)
119 foundopts[name] = (ctype, default)
120 carryover = ''
120 carryover = ''
121 else:
121 else:
122 m = re.search(configpartialre, line)
122 m = re.search(configpartialre, line)
123 if m:
123 if m:
124 carryover = line
124 carryover = line
125 else:
125 else:
126 carryover = ''
126 carryover = ''
127
127
128 for name in sorted(foundopts):
128 for name in sorted(foundopts):
129 if name not in documented:
129 if name not in documented:
130 if not (name.startswith("devel.") or
130 if not (name.startswith("devel.") or
131 name.startswith("experimental.") or
131 name.startswith("experimental.") or
132 name.startswith("debug.")):
132 name.startswith("debug.")):
133 ctype, default = foundopts[name]
133 ctype, default = foundopts[name]
134 if default:
134 if default:
135 default = ' [%s]' % default
135 default = ' [%s]' % default
136 print("undocumented: %s (%s)%s" % (name, ctype, default))
136 print("undocumented: %s (%s)%s" % (name, ctype, default))
137
137
138 if __name__ == "__main__":
138 if __name__ == "__main__":
139 if len(sys.argv) > 1:
139 if len(sys.argv) > 1:
140 sys.exit(main(sys.argv[1:]))
140 sys.exit(main(sys.argv[1:]))
141 else:
141 else:
142 sys.exit(main([l.rstrip() for l in sys.stdin]))
142 sys.exit(main([l.rstrip() for l in sys.stdin]))
General Comments 0
You need to be logged in to leave comments. Login now