check-config.py
189 lines
| 5.5 KiB
| text/x-python
|
PythonLexer
/ contrib / check-config.py
Gregory Szorc
|
r46434 | #!/usr/bin/env python3 | ||
Matt Mackall
|
r25790 | # | ||
# check-config - a config flag documentation checker for Mercurial | ||||
# | ||||
Raphaël Gomès
|
r47575 | # Copyright 2015 Olivia Mackall <olivia@selenic.com> | ||
Matt Mackall
|
r25790 | # | ||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
import re | ||||
import sys | ||||
foundopts = {} | ||||
documented = {} | ||||
Gregory Szorc
|
r33192 | allowinconsistent = set() | ||
Matt Mackall
|
r25790 | |||
Augie Fackler
|
r43346 | configre = re.compile( | ||
br''' | ||||
Gregory Szorc
|
r32847 | # Function call | ||
Gregory Szorc
|
r32848 | ui\.config(?P<ctype>|int|bool|list)\( | ||
Gregory Szorc
|
r32847 | # First argument. | ||
Gregory Szorc
|
r32848 | ['"](?P<section>\S+)['"],\s* | ||
Gregory Szorc
|
r32847 | # Second argument | ||
Gregory Szorc
|
r32848 | ['"](?P<option>\S+)['"](,\s+ | ||
(?:default=)?(?P<default>\S+?))? | ||||
Augie Fackler
|
r43346 | \)''', | ||
re.VERBOSE | re.MULTILINE, | ||||
) | ||||
Gregory Szorc
|
r32847 | |||
Augie Fackler
|
r43346 | configwithre = re.compile( | ||
br''' | ||||
Gregory Szorc
|
r32849 | ui\.config(?P<ctype>with)\( | ||
# First argument is callback function. This doesn't parse robustly | ||||
# if it is e.g. a function call. | ||||
[^,]+,\s* | ||||
['"](?P<section>\S+)['"],\s* | ||||
['"](?P<option>\S+)['"](,\s+ | ||||
(?:default=)?(?P<default>\S+?))? | ||||
Augie Fackler
|
r43346 | \)''', | ||
re.VERBOSE | re.MULTILINE, | ||||
) | ||||
Gregory Szorc
|
r32849 | |||
Augie Fackler
|
r43346 | configpartialre = br"""ui\.config""" | ||
Matt Mackall
|
r25790 | |||
Augie Fackler
|
r43346 | ignorere = re.compile( | ||
br''' | ||||
Gregory Szorc
|
r33192 | \#\s(?P<reason>internal|experimental|deprecated|developer|inconsistent)\s | ||
config:\s(?P<config>\S+\.\S+)$ | ||||
Augie Fackler
|
r43346 | ''', | ||
re.VERBOSE | re.MULTILINE, | ||||
) | ||||
Gregory Szorc
|
r33192 | |||
Augie Fackler
|
r40295 | if sys.version_info[0] > 2: | ||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r40295 | def mkstr(b): | ||
if isinstance(b, str): | ||||
return b | ||||
return b.decode('utf8') | ||||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r40295 | else: | ||
mkstr = lambda x: x | ||||
Augie Fackler
|
r43346 | |||
Matt Mackall
|
r25790 | def main(args): | ||
for f in args: | ||||
Pulkit Goyal
|
r35938 | sect = b'' | ||
prevname = b'' | ||||
confsect = b'' | ||||
carryover = b'' | ||||
Ryan McElroy
|
r33571 | linenum = 0 | ||
Pulkit Goyal
|
r35937 | for l in open(f, 'rb'): | ||
Ryan McElroy
|
r33571 | linenum += 1 | ||
Matt Mackall
|
r25790 | |||
# check topic-like bits | ||||
Gregory Szorc
|
r41682 | m = re.match(br'\s*``(\S+)``', l) | ||
Matt Mackall
|
r25790 | if m: | ||
prevname = m.group(1) | ||||
Gregory Szorc
|
r41682 | if re.match(br'^\s*-+$', l): | ||
Matt Mackall
|
r25790 | sect = prevname | ||
Pulkit Goyal
|
r35938 | prevname = b'' | ||
Matt Mackall
|
r25790 | |||
if sect and prevname: | ||||
Pulkit Goyal
|
r35938 | name = sect + b'.' + prevname | ||
Matt Mackall
|
r25790 | documented[name] = 1 | ||
# check docstring bits | ||||
Pulkit Goyal
|
r35938 | m = re.match(br'^\s+\[(\S+)\]', l) | ||
Matt Mackall
|
r25790 | if m: | ||
confsect = m.group(1) | ||||
continue | ||||
Pulkit Goyal
|
r35938 | m = re.match(br'^\s+(?:#\s*)?(\S+) = ', l) | ||
Matt Mackall
|
r25790 | if m: | ||
Pulkit Goyal
|
r35938 | name = confsect + b'.' + m.group(1) | ||
Matt Mackall
|
r25790 | documented[name] = 1 | ||
# like the bugzilla extension | ||||
Pulkit Goyal
|
r35938 | m = re.match(br'^\s*(\S+\.\S+)$', l) | ||
Matt Mackall
|
r25790 | if m: | ||
documented[m.group(1)] = 1 | ||||
timeless
|
r27310 | # like convert | ||
Pulkit Goyal
|
r35938 | m = re.match(br'^\s*:(\S+\.\S+):\s+', l) | ||
timeless
|
r27310 | if m: | ||
documented[m.group(1)] = 1 | ||||
Matt Mackall
|
r25790 | # quoted in help or docstrings | ||
Pulkit Goyal
|
r35938 | m = re.match(br'.*?``(\S+\.\S+)``', l) | ||
Matt Mackall
|
r25790 | if m: | ||
documented[m.group(1)] = 1 | ||||
# look for ignore markers | ||||
Gregory Szorc
|
r33192 | m = ignorere.search(l) | ||
Matt Mackall
|
r25790 | if m: | ||
Augie Fackler
|
r40295 | if m.group('reason') == b'inconsistent': | ||
Gregory Szorc
|
r33192 | allowinconsistent.add(m.group('config')) | ||
else: | ||||
documented[m.group('config')] = 1 | ||||
Matt Mackall
|
r25790 | |||
# look for code-like bits | ||||
timeless
|
r27313 | line = carryover + l | ||
Gregory Szorc
|
r32849 | m = configre.search(line) or configwithre.search(line) | ||
Matt Mackall
|
r25790 | if m: | ||
Gregory Szorc
|
r32848 | ctype = m.group('ctype') | ||
Matt Mackall
|
r25790 | if not ctype: | ||
ctype = 'str' | ||||
Matt Harbison
|
r39744 | name = m.group('section') + b"." + m.group('option') | ||
Gregory Szorc
|
r32848 | default = m.group('default') | ||
Augie Fackler
|
r40295 | if default in ( | ||
Augie Fackler
|
r43346 | None, | ||
b'False', | ||||
b'None', | ||||
b'0', | ||||
b'[]', | ||||
b'""', | ||||
b"''", | ||||
): | ||||
Matt Harbison
|
r39744 | default = b'' | ||
Pulkit Goyal
|
r35938 | if re.match(b'[a-z.]+$', default): | ||
Matt Harbison
|
r39744 | default = b'<variable>' | ||
Augie Fackler
|
r43346 | if ( | ||
name in foundopts | ||||
and (ctype, default) != foundopts[name] | ||||
and name not in allowinconsistent | ||||
): | ||||
Augie Fackler
|
r40295 | print(mkstr(l.rstrip())) | ||
fctype, fdefault = foundopts[name] | ||||
Augie Fackler
|
r43346 | print( | ||
"conflict on %s: %r != %r" | ||||
% ( | ||||
mkstr(name), | ||||
(mkstr(ctype), mkstr(default)), | ||||
(mkstr(fctype), mkstr(fdefault)), | ||||
) | ||||
) | ||||
Augie Fackler
|
r40295 | print("at %s:%d:" % (mkstr(f), linenum)) | ||
Matt Mackall
|
r25790 | foundopts[name] = (ctype, default) | ||
Matt Harbison
|
r39744 | carryover = b'' | ||
timeless
|
r27313 | else: | ||
m = re.search(configpartialre, line) | ||||
if m: | ||||
carryover = line | ||||
else: | ||||
Matt Harbison
|
r39744 | carryover = b'' | ||
Matt Mackall
|
r25790 | |||
for name in sorted(foundopts): | ||||
if name not in documented: | ||||
Augie Fackler
|
r43346 | if not ( | ||
name.startswith(b"devel.") | ||||
or name.startswith(b"experimental.") | ||||
or name.startswith(b"debug.") | ||||
): | ||||
Matt Mackall
|
r25790 | ctype, default = foundopts[name] | ||
if default: | ||||
Augie Fackler
|
r40295 | if isinstance(default, bytes): | ||
default = mkstr(default) | ||||
Matt Mackall
|
r25790 | default = ' [%s]' % default | ||
Augie Fackler
|
r40295 | elif isinstance(default, bytes): | ||
default = mkstr(default) | ||||
Augie Fackler
|
r43346 | print( | ||
"undocumented: %s (%s)%s" | ||||
% (mkstr(name), mkstr(ctype), default) | ||||
) | ||||
Matt Mackall
|
r25790 | |||
if __name__ == "__main__": | ||||
FUJIWARA Katsunori
|
r27992 | if len(sys.argv) > 1: | ||
sys.exit(main(sys.argv[1:])) | ||||
else: | ||||
sys.exit(main([l.rstrip() for l in sys.stdin])) | ||||