Mercurial > hg
changeset 25790:db5b6a1c064d
check-config: add config option checker
This script scans files for lines that look like either ui.config
usage or config variable documentation. It then ensures:
- ui.config calls for each option agree on types and defaults
- every option appears to be mentioned in documentation
It doesn't complain about devel/experimental options and allows
marking options that are not intended to be public.
Since we haven't been able to come up with a good scheme for
documenting config options at point of use, this will help close the
loop of making sure all options that should be documented are.
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Thu, 25 Jun 2015 17:34:53 -0500 |
parents | 95dc4b009f60 |
children | 917be0574d7f |
files | contrib/check-config.py |
diffstat | 1 files changed, 95 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/check-config.py Thu Jun 25 17:34:53 2015 -0500 @@ -0,0 +1,95 @@ +#!/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 = {} + +configre = (r"""ui\.config(|int|bool|list)\(['"](\S+)['"], ?""" + r"""['"](\S+)['"](,\s(?:default=)?(\S+?))?\)""") + +def main(args): + for f in args: + sect = '' + prevname = '' + confsect = '' + for l in open(f): + + # check topic-like bits + m = re.match('\s*``(\S+)``', l) + if m: + prevname = m.group(1) + continue + if re.match('^\s*-+$', l): + sect = prevname + prevname = '' + continue + + 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 + m = re.match(r'^\s+(?:#\s*)?([a-z._]+) = ', l) + if m: + name = confsect + '.' + m.group(1) + documented[name] = 1 + + # like the bugzilla extension + m = re.match(r'^\s*([a-z]+\.[a-z]+)$', l) + if m: + documented[m.group(1)] = 1 + + # quoted in help or docstrings + m = re.match(r'.*?``([-a-z_]+\.[-a-z_]+)``', l) + if m: + documented[m.group(1)] = 1 + + # look for ignore markers + m = re.search(r'# (?:internal|experimental|deprecated|developer)' + ' config: (\S+.\S+)$', l) + if m: + documented[m.group(1)] = 1 + + # look for code-like bits + m = re.search(configre, l) + 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) + + 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:]))