view contrib/check-config.py @ 27319:b64b6fdc5c9b

discovery: properly filter changeset in 'peer.known' (issue4982) The 'peer.known' call (handled at the repository level) was applying its own manual filtering (looking at phases) instead of relying on the repoview mechanism. This led to the discovery finding more "common" node that 'getbundle' was willing to recognised. From there, bad things happen, issue4982 is a symptom of it. While situations like described in issue4982 can still happen because of race conditions, fixing 'peer.known' is important for consistency in all cases. We update the code to use 'repoview' filtering. This lead to small changes in the tests for exchanging obsolescence marker because the discovery yields different results. The test affected in 'test-obsolete-changeset-exchange.t' is a test for issue4982 getting back to its expected state.
author Pierre-Yves David <pierre-yves.david@fb.com>
date Wed, 02 Dec 2015 16:12:15 -0800
parents 9d155accd8f1
children 8f244b75cc5e
line wrap: on
line source

#!/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+)['"],\s*"""
            r"""['"](\S+)['"](,\s+(?:default=)?(\S+?))?\)""")
configpartialre = (r"""ui\.config""")

def main(args):
    for f in args:
        sect = ''
        prevname = ''
        confsect = ''
        carryover = ''
        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
            m = re.match(r'^\s+(?:#\s*)?(\S+) = ', l)
            if m:
                name = confsect + '.' + m.group(1)
                documented[name] = 1

            # like the bugzilla extension
            m = re.match(r'^\s*(\S+\.\S+)$', l)
            if m:
                documented[m.group(1)] = 1

            # like convert
            m = re.match(r'^\s*:(\S+\.\S+):\s+', l)
            if m:
                documented[m.group(1)] = 1

            # quoted in help or docstrings
            m = re.match(r'.*?``(\S+\.\S+)``', 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
            line = carryover + l
            m = re.search(configre, line, re.MULTILINE)
            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)
                carryover = ''
            else:
                m = re.search(configpartialre, line)
                if m:
                    carryover = line
                else:
                    carryover = ''

    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:]))