[PATCH 1 of 4] check-config: add config option checker

Augie Fackler raf at durin42.com
Wed Jul 15 10:46:58 CDT 2015


On Mon, Jul 06, 2015 at 05:58:27PM -0500, Matt Mackall wrote:
> # HG changeset patch
> # User Matt Mackall <mpm at selenic.com>
> # Date 1435271693 18000
> #      Thu Jun 25 17:34:53 2015 -0500
> # Node ID 1190e5ca97d5f4688270f5c1d73e14f43d0332b3
> # Parent  ff5172c830022b64cc5bd1bae36b2276e9dc6e5d
> 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.
>
> diff -r ff5172c83002 -r 1190e5ca97d5 contrib/check-config.py
> --- /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,96 @@
> +#!/usr/bin/env python
> +#
> +# check-config - a config flag documentation checker for Mercurial
> +#
> +# Copyright 2015 Matt Mackall <mpm at 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 os
> +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):

nit: this could probably stand to be a with statement, otherwise
running this script in a non-refcounted python (pypy) might run out of
fds.

(I'm happy to do this as a followup though, it's certainly far from pressing.)

> +
> +            # 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:]))
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel


More information about the Mercurial-devel mailing list