check-seclevel.py
170 lines
| 5.7 KiB
| text/x-python
|
PythonLexer
/ doc / check-seclevel.py
|
r17648 | #!/usr/bin/env python | ||
# | ||||
# checkseclevel - checking section title levels in each online help documents | ||||
import sys, os | ||||
import optparse | ||||
# import from the live mercurial repo | ||||
sys.path.insert(0, "..") | ||||
# fall back to pure modules if required C extensions are not available | ||||
sys.path.append(os.path.join('..', 'mercurial', 'pure')) | ||||
from mercurial import demandimport; demandimport.enable() | ||||
from mercurial.commands import table | ||||
from mercurial.help import helptable | ||||
from mercurial import extensions | ||||
from mercurial import minirst | ||||
from mercurial import util | ||||
_verbose = False | ||||
def verbose(msg): | ||||
if _verbose: | ||||
print msg | ||||
def error(msg): | ||||
sys.stderr.write('%s\n' % msg) | ||||
level2mark = ['"', '=', '-', '.', '#'] | ||||
reservedmarks = ['"'] | ||||
mark2level = {} | ||||
for m, l in zip(level2mark, xrange(len(level2mark))): | ||||
if m not in reservedmarks: | ||||
mark2level[m] = l | ||||
initlevel_topic = 0 | ||||
initlevel_cmd = 1 | ||||
initlevel_ext = 1 | ||||
initlevel_ext_cmd = 3 | ||||
def showavailables(initlevel): | ||||
error(' available marks and order of them in this help: %s' % | ||||
(', '.join(['%r' % (m * 4) for m in level2mark[initlevel + 1:]]))) | ||||
def checkseclevel(doc, name, initlevel): | ||||
verbose('checking "%s"' % name) | ||||
blocks, pruned = minirst.parse(doc, 0, ['verbose']) | ||||
errorcnt = 0 | ||||
curlevel = initlevel | ||||
for block in blocks: | ||||
if block['type'] != 'section': | ||||
continue | ||||
mark = block['underline'] | ||||
title = block['lines'][0] | ||||
if (mark not in mark2level) or (mark2level[mark] <= initlevel): | ||||
error('invalid section mark %r for "%s" of %s' % | ||||
(mark * 4, title, name)) | ||||
showavailables(initlevel) | ||||
errorcnt += 1 | ||||
continue | ||||
nextlevel = mark2level[mark] | ||||
if curlevel < nextlevel and curlevel + 1 != nextlevel: | ||||
error('gap of section level at "%s" of %s' % | ||||
(title, name)) | ||||
showavailables(initlevel) | ||||
errorcnt += 1 | ||||
continue | ||||
verbose('appropriate section level for "%s %s"' % | ||||
(mark * (nextlevel * 2), title)) | ||||
curlevel = nextlevel | ||||
return errorcnt | ||||
def checkcmdtable(cmdtable, namefmt, initlevel): | ||||
errorcnt = 0 | ||||
for k, entry in cmdtable.items(): | ||||
name = k.split("|")[0].lstrip("^") | ||||
if not entry[0].__doc__: | ||||
verbose('skip checking %s: no help document' % | ||||
(namefmt % name)) | ||||
continue | ||||
errorcnt += checkseclevel(entry[0].__doc__, | ||||
namefmt % name, | ||||
initlevel) | ||||
return errorcnt | ||||
def checkhghelps(): | ||||
errorcnt = 0 | ||||
for names, sec, doc in helptable: | ||||
if util.safehasattr(doc, '__call__'): | ||||
doc = doc() | ||||
errorcnt += checkseclevel(doc, | ||||
'%s help topic' % names[0], | ||||
initlevel_topic) | ||||
errorcnt += checkcmdtable(table, '%s command', initlevel_cmd) | ||||
for name in sorted(extensions.enabled().keys() + | ||||
extensions.disabled().keys()): | ||||
mod = extensions.load(None, name, None) | ||||
if not mod.__doc__: | ||||
verbose('skip checking %s extension: no help document' % name) | ||||
continue | ||||
errorcnt += checkseclevel(mod.__doc__, | ||||
'%s extension' % name, | ||||
initlevel_ext) | ||||
cmdtable = getattr(mod, 'cmdtable', None) | ||||
if cmdtable: | ||||
errorcnt += checkcmdtable(cmdtable, | ||||
'%s command of ' + name + ' extension', | ||||
initlevel_ext_cmd) | ||||
return errorcnt | ||||
def checkfile(filename, initlevel): | ||||
if filename == '-': | ||||
filename = 'stdin' | ||||
doc = sys.stdin.read() | ||||
else: | ||||
fp = open(filename) | ||||
try: | ||||
doc = fp.read() | ||||
finally: | ||||
fp.close() | ||||
verbose('checking input from %s with initlevel %d' % | ||||
(filename, initlevel)) | ||||
return checkseclevel(doc, 'input from %s' % filename, initlevel) | ||||
if __name__ == "__main__": | ||||
optparser = optparse.OptionParser("""%prog [options] | ||||
This checks all help documents of Mercurial (topics, commands, | ||||
extensions and commands of them), if no file is specified by --file | ||||
option. | ||||
""") | ||||
optparser.add_option("-v", "--verbose", | ||||
help="enable additional output", | ||||
action="store_true") | ||||
optparser.add_option("-f", "--file", | ||||
help="filename to read in (or '-' for stdin)", | ||||
action="store", default="") | ||||
optparser.add_option("-t", "--topic", | ||||
help="parse file as help topic", | ||||
action="store_const", dest="initlevel", const=0) | ||||
optparser.add_option("-c", "--command", | ||||
help="parse file as help of core command", | ||||
action="store_const", dest="initlevel", const=1) | ||||
optparser.add_option("-e", "--extension", | ||||
help="parse file as help of extension", | ||||
action="store_const", dest="initlevel", const=1) | ||||
optparser.add_option("-C", "--extension-command", | ||||
help="parse file as help of extension command", | ||||
action="store_const", dest="initlevel", const=3) | ||||
optparser.add_option("-l", "--initlevel", | ||||
help="set initial section level manually", | ||||
action="store", type="int", default=0) | ||||
(options, args) = optparser.parse_args() | ||||
_verbose = options.verbose | ||||
if options.file: | ||||
if checkfile(options.file, options.initlevel): | ||||
sys.exit(1) | ||||
else: | ||||
if checkhghelps(): | ||||
sys.exit(1) | ||||