check-config.py
108 lines
| 3.5 KiB
| text/x-python
|
PythonLexer
/ contrib / check-config.py
Matt Mackall
|
r25790 | #!/usr/bin/env python | ||
# | ||||
# check-config - a config flag documentation checker for Mercurial | ||||
# | ||||
# Copyright 2015 Matt Mackall <mpm@selenic.com> | ||||
# | ||||
# 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 = {} | ||||
timeless
|
r27313 | configre = (r"""ui\.config(|int|bool|list)\(['"](\S+)['"],\s*""" | ||
r"""['"](\S+)['"](,\s+(?:default=)?(\S+?))?\)""") | ||||
configpartialre = (r"""ui\.config""") | ||||
Matt Mackall
|
r25790 | |||
def main(args): | ||||
for f in args: | ||||
sect = '' | ||||
prevname = '' | ||||
confsect = '' | ||||
timeless
|
r27313 | carryover = '' | ||
Matt Mackall
|
r25790 | for l in open(f): | ||
# check topic-like bits | ||||
m = re.match('\s*``(\S+)``', l) | ||||
if m: | ||||
prevname = m.group(1) | ||||
if re.match('^\s*-+$', l): | ||||
sect = prevname | ||||
prevname = '' | ||||
if sect and prevname: | ||||
name = sect + '.' + prevname | ||||
documented[name] = 1 | ||||
# check docstring bits | ||||
m = re.match(r'^\s+\[(\S+)\]', l) | ||||
if m: | ||||
confsect = m.group(1) | ||||
continue | ||||
timeless
|
r27311 | m = re.match(r'^\s+(?:#\s*)?(\S+) = ', l) | ||
Matt Mackall
|
r25790 | if m: | ||
name = confsect + '.' + m.group(1) | ||||
documented[name] = 1 | ||||
# like the bugzilla extension | ||||
timeless
|
r27311 | m = re.match(r'^\s*(\S+\.\S+)$', l) | ||
Matt Mackall
|
r25790 | if m: | ||
documented[m.group(1)] = 1 | ||||
timeless
|
r27310 | # like convert | ||
m = re.match(r'^\s*:(\S+\.\S+):\s+', l) | ||||
if m: | ||||
documented[m.group(1)] = 1 | ||||
Matt Mackall
|
r25790 | # quoted in help or docstrings | ||
timeless
|
r27311 | m = re.match(r'.*?``(\S+\.\S+)``', l) | ||
Matt Mackall
|
r25790 | if m: | ||
documented[m.group(1)] = 1 | ||||
# look for ignore markers | ||||
m = re.search(r'# (?:internal|experimental|deprecated|developer)' | ||||
timeless
|
r27312 | ' config: (\S+\.\S+)$', l) | ||
Matt Mackall
|
r25790 | if m: | ||
documented[m.group(1)] = 1 | ||||
# look for code-like bits | ||||
timeless
|
r27313 | line = carryover + l | ||
m = re.search(configre, line, re.MULTILINE) | ||||
Matt Mackall
|
r25790 | if m: | ||
ctype = m.group(1) | ||||
if not ctype: | ||||
ctype = 'str' | ||||
name = m.group(2) + "." + m.group(3) | ||||
default = m.group(5) | ||||
if default in (None, 'False', 'None', '0', '[]', '""', "''"): | ||||
default = '' | ||||
if re.match('[a-z.]+$', default): | ||||
default = '<variable>' | ||||
if name in foundopts and (ctype, default) != foundopts[name]: | ||||
print l | ||||
print "conflict on %s: %r != %r" % (name, (ctype, default), | ||||
foundopts[name]) | ||||
foundopts[name] = (ctype, default) | ||||
timeless
|
r27313 | carryover = '' | ||
else: | ||||
m = re.search(configpartialre, line) | ||||
if m: | ||||
carryover = line | ||||
else: | ||||
carryover = '' | ||||
Matt Mackall
|
r25790 | |||
for name in sorted(foundopts): | ||||
if name not in documented: | ||||
if not (name.startswith("devel.") or | ||||
name.startswith("experimental.") or | ||||
name.startswith("debug.")): | ||||
ctype, default = foundopts[name] | ||||
if default: | ||||
default = ' [%s]' % default | ||||
print "undocumented: %s (%s)%s" % (name, ctype, default) | ||||
if __name__ == "__main__": | ||||
sys.exit(main(sys.argv[1:])) | ||||