contrib/check-config.py
changeset 25790 db5b6a1c064d
child 25849 d1cb185b9ee2
equal deleted inserted replaced
25789:95dc4b009f60 25790:db5b6a1c064d
       
     1 #!/usr/bin/env python
       
     2 #
       
     3 # check-config - a config flag documentation checker for Mercurial
       
     4 #
       
     5 # Copyright 2015 Matt Mackall <mpm@selenic.com>
       
     6 #
       
     7 # This software may be used and distributed according to the terms of the
       
     8 # GNU General Public License version 2 or any later version.
       
     9 
       
    10 import re
       
    11 import sys
       
    12 
       
    13 foundopts = {}
       
    14 documented = {}
       
    15 
       
    16 configre = (r"""ui\.config(|int|bool|list)\(['"](\S+)['"], ?"""
       
    17             r"""['"](\S+)['"](,\s(?:default=)?(\S+?))?\)""")
       
    18 
       
    19 def main(args):
       
    20     for f in args:
       
    21         sect = ''
       
    22         prevname = ''
       
    23         confsect = ''
       
    24         for l in open(f):
       
    25 
       
    26             # check topic-like bits
       
    27             m = re.match('\s*``(\S+)``', l)
       
    28             if m:
       
    29                 prevname = m.group(1)
       
    30                 continue
       
    31             if re.match('^\s*-+$', l):
       
    32                 sect = prevname
       
    33                 prevname = ''
       
    34                 continue
       
    35 
       
    36             if sect and prevname:
       
    37                 name = sect + '.' + prevname
       
    38                 documented[name] = 1
       
    39 
       
    40             # check docstring bits
       
    41             m = re.match(r'^\s+\[(\S+)\]', l)
       
    42             if m:
       
    43                 confsect = m.group(1)
       
    44                 continue
       
    45             m = re.match(r'^\s+(?:#\s*)?([a-z._]+) = ', l)
       
    46             if m:
       
    47                 name = confsect + '.' + m.group(1)
       
    48                 documented[name] = 1
       
    49 
       
    50             # like the bugzilla extension
       
    51             m = re.match(r'^\s*([a-z]+\.[a-z]+)$', l)
       
    52             if m:
       
    53                 documented[m.group(1)] = 1
       
    54 
       
    55             # quoted in help or docstrings
       
    56             m = re.match(r'.*?``([-a-z_]+\.[-a-z_]+)``', l)
       
    57             if m:
       
    58                 documented[m.group(1)] = 1
       
    59 
       
    60             # look for ignore markers
       
    61             m = re.search(r'# (?:internal|experimental|deprecated|developer)'
       
    62                           ' config: (\S+.\S+)$', l)
       
    63             if m:
       
    64                 documented[m.group(1)] = 1
       
    65 
       
    66             # look for code-like bits
       
    67             m = re.search(configre, l)
       
    68             if m:
       
    69                 ctype = m.group(1)
       
    70                 if not ctype:
       
    71                     ctype = 'str'
       
    72                 name = m.group(2) + "." + m.group(3)
       
    73                 default = m.group(5)
       
    74                 if default in (None, 'False', 'None', '0', '[]', '""', "''"):
       
    75                     default = ''
       
    76                 if re.match('[a-z.]+$', default):
       
    77                     default = '<variable>'
       
    78                 if name in foundopts and (ctype, default) != foundopts[name]:
       
    79                     print l
       
    80                     print "conflict on %s: %r != %r" % (name, (ctype, default),
       
    81                                                         foundopts[name])
       
    82                 foundopts[name] = (ctype, default)
       
    83 
       
    84     for name in sorted(foundopts):
       
    85         if name not in documented:
       
    86             if not (name.startswith("devel.") or
       
    87                     name.startswith("experimental.") or
       
    88                     name.startswith("debug.")):
       
    89                 ctype, default = foundopts[name]
       
    90                 if default:
       
    91                     default = ' [%s]' % default
       
    92                 print "undocumented: %s (%s)%s" % (name, ctype, default)
       
    93 
       
    94 if __name__ == "__main__":
       
    95     sys.exit(main(sys.argv[1:]))