##// END OF EJS Templates
check-config: use compiled regexp...
Gregory Szorc -
r32847:e5a6a540 default
parent child Browse files
Show More
@@ -1,112 +1,120 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
16
17 configre = (r"""ui\.config(|int|bool|list)\(['"](\S+)['"],\s*"""
17 configre = re.compile(r'''
18 r"""['"](\S+)['"](,\s+(?:default=)?(\S+?))?\)""")
18 # Function call
19 ui\.config(|int|bool|list)\(
20 # First argument.
21 ['"](\S+)['"],\s*
22 # Second argument
23 ['"](\S+)['"](,\s+
24 (?:default=)?(\S+?))?
25 \)''', re.VERBOSE | re.MULTILINE)
26
19 configpartialre = (r"""ui\.config""")
27 configpartialre = (r"""ui\.config""")
20
28
21 def main(args):
29 def main(args):
22 for f in args:
30 for f in args:
23 sect = ''
31 sect = ''
24 prevname = ''
32 prevname = ''
25 confsect = ''
33 confsect = ''
26 carryover = ''
34 carryover = ''
27 for l in open(f):
35 for l in open(f):
28
36
29 # check topic-like bits
37 # check topic-like bits
30 m = re.match('\s*``(\S+)``', l)
38 m = re.match('\s*``(\S+)``', l)
31 if m:
39 if m:
32 prevname = m.group(1)
40 prevname = m.group(1)
33 if re.match('^\s*-+$', l):
41 if re.match('^\s*-+$', l):
34 sect = prevname
42 sect = prevname
35 prevname = ''
43 prevname = ''
36
44
37 if sect and prevname:
45 if sect and prevname:
38 name = sect + '.' + prevname
46 name = sect + '.' + prevname
39 documented[name] = 1
47 documented[name] = 1
40
48
41 # check docstring bits
49 # check docstring bits
42 m = re.match(r'^\s+\[(\S+)\]', l)
50 m = re.match(r'^\s+\[(\S+)\]', l)
43 if m:
51 if m:
44 confsect = m.group(1)
52 confsect = m.group(1)
45 continue
53 continue
46 m = re.match(r'^\s+(?:#\s*)?(\S+) = ', l)
54 m = re.match(r'^\s+(?:#\s*)?(\S+) = ', l)
47 if m:
55 if m:
48 name = confsect + '.' + m.group(1)
56 name = confsect + '.' + m.group(1)
49 documented[name] = 1
57 documented[name] = 1
50
58
51 # like the bugzilla extension
59 # like the bugzilla extension
52 m = re.match(r'^\s*(\S+\.\S+)$', l)
60 m = re.match(r'^\s*(\S+\.\S+)$', l)
53 if m:
61 if m:
54 documented[m.group(1)] = 1
62 documented[m.group(1)] = 1
55
63
56 # like convert
64 # like convert
57 m = re.match(r'^\s*:(\S+\.\S+):\s+', l)
65 m = re.match(r'^\s*:(\S+\.\S+):\s+', l)
58 if m:
66 if m:
59 documented[m.group(1)] = 1
67 documented[m.group(1)] = 1
60
68
61 # quoted in help or docstrings
69 # quoted in help or docstrings
62 m = re.match(r'.*?``(\S+\.\S+)``', l)
70 m = re.match(r'.*?``(\S+\.\S+)``', l)
63 if m:
71 if m:
64 documented[m.group(1)] = 1
72 documented[m.group(1)] = 1
65
73
66 # look for ignore markers
74 # look for ignore markers
67 m = re.search(r'# (?:internal|experimental|deprecated|developer)'
75 m = re.search(r'# (?:internal|experimental|deprecated|developer)'
68 ' config: (\S+\.\S+)$', l)
76 ' config: (\S+\.\S+)$', l)
69 if m:
77 if m:
70 documented[m.group(1)] = 1
78 documented[m.group(1)] = 1
71
79
72 # look for code-like bits
80 # look for code-like bits
73 line = carryover + l
81 line = carryover + l
74 m = re.search(configre, line, re.MULTILINE)
82 m = configre.search(line)
75 if m:
83 if m:
76 ctype = m.group(1)
84 ctype = m.group(1)
77 if not ctype:
85 if not ctype:
78 ctype = 'str'
86 ctype = 'str'
79 name = m.group(2) + "." + m.group(3)
87 name = m.group(2) + "." + m.group(3)
80 default = m.group(5)
88 default = m.group(5)
81 if default in (None, 'False', 'None', '0', '[]', '""', "''"):
89 if default in (None, 'False', 'None', '0', '[]', '""', "''"):
82 default = ''
90 default = ''
83 if re.match('[a-z.]+$', default):
91 if re.match('[a-z.]+$', default):
84 default = '<variable>'
92 default = '<variable>'
85 if name in foundopts and (ctype, default) != foundopts[name]:
93 if name in foundopts and (ctype, default) != foundopts[name]:
86 print(l)
94 print(l)
87 print("conflict on %s: %r != %r" % (name, (ctype, default),
95 print("conflict on %s: %r != %r" % (name, (ctype, default),
88 foundopts[name]))
96 foundopts[name]))
89 foundopts[name] = (ctype, default)
97 foundopts[name] = (ctype, default)
90 carryover = ''
98 carryover = ''
91 else:
99 else:
92 m = re.search(configpartialre, line)
100 m = re.search(configpartialre, line)
93 if m:
101 if m:
94 carryover = line
102 carryover = line
95 else:
103 else:
96 carryover = ''
104 carryover = ''
97
105
98 for name in sorted(foundopts):
106 for name in sorted(foundopts):
99 if name not in documented:
107 if name not in documented:
100 if not (name.startswith("devel.") or
108 if not (name.startswith("devel.") or
101 name.startswith("experimental.") or
109 name.startswith("experimental.") or
102 name.startswith("debug.")):
110 name.startswith("debug.")):
103 ctype, default = foundopts[name]
111 ctype, default = foundopts[name]
104 if default:
112 if default:
105 default = ' [%s]' % default
113 default = ' [%s]' % default
106 print("undocumented: %s (%s)%s" % (name, ctype, default))
114 print("undocumented: %s (%s)%s" % (name, ctype, default))
107
115
108 if __name__ == "__main__":
116 if __name__ == "__main__":
109 if len(sys.argv) > 1:
117 if len(sys.argv) > 1:
110 sys.exit(main(sys.argv[1:]))
118 sys.exit(main(sys.argv[1:]))
111 else:
119 else:
112 sys.exit(main([l.rstrip() for l in sys.stdin]))
120 sys.exit(main([l.rstrip() for l in sys.stdin]))
General Comments 0
You need to be logged in to leave comments. Login now