comparison doc/check-seclevel.py @ 17648:07f1ac17b722

doc: add the tool to check section marks in help documents This patch adds "doc/check-seclevel.py" which checks below in help documents: - whether unknown or unavailable section marks are used or not - whether appropriate section mark is used at sub-sectioning It should be invoked in "doc" directory. It checks all help documents of Mercurial (topics, commands, extensions), if no file is specified by --file option. With --file option, it checks contents of the specified file as help document, for self testing purpose: -t/-c/-e/-C are used to specify what kind of help document contents of the specified file is. This checking is related to changeset 979b107eaea2.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Mon, 13 Aug 2012 21:25:48 +0900
parents
children e15c991fe2ec
comparison
equal deleted inserted replaced
17647:d34ba4991188 17648:07f1ac17b722
1 #!/usr/bin/env python
2 #
3 # checkseclevel - checking section title levels in each online help documents
4
5 import sys, os
6 import optparse
7
8 # import from the live mercurial repo
9 sys.path.insert(0, "..")
10 # fall back to pure modules if required C extensions are not available
11 sys.path.append(os.path.join('..', 'mercurial', 'pure'))
12 from mercurial import demandimport; demandimport.enable()
13 from mercurial.commands import table
14 from mercurial.help import helptable
15 from mercurial import extensions
16 from mercurial import minirst
17 from mercurial import util
18
19 _verbose = False
20
21 def verbose(msg):
22 if _verbose:
23 print msg
24
25 def error(msg):
26 sys.stderr.write('%s\n' % msg)
27
28 level2mark = ['"', '=', '-', '.', '#']
29 reservedmarks = ['"']
30
31 mark2level = {}
32 for m, l in zip(level2mark, xrange(len(level2mark))):
33 if m not in reservedmarks:
34 mark2level[m] = l
35
36 initlevel_topic = 0
37 initlevel_cmd = 1
38 initlevel_ext = 1
39 initlevel_ext_cmd = 3
40
41 def showavailables(initlevel):
42 error(' available marks and order of them in this help: %s' %
43 (', '.join(['%r' % (m * 4) for m in level2mark[initlevel + 1:]])))
44
45 def checkseclevel(doc, name, initlevel):
46 verbose('checking "%s"' % name)
47 blocks, pruned = minirst.parse(doc, 0, ['verbose'])
48 errorcnt = 0
49 curlevel = initlevel
50 for block in blocks:
51 if block['type'] != 'section':
52 continue
53 mark = block['underline']
54 title = block['lines'][0]
55 if (mark not in mark2level) or (mark2level[mark] <= initlevel):
56 error('invalid section mark %r for "%s" of %s' %
57 (mark * 4, title, name))
58 showavailables(initlevel)
59 errorcnt += 1
60 continue
61 nextlevel = mark2level[mark]
62 if curlevel < nextlevel and curlevel + 1 != nextlevel:
63 error('gap of section level at "%s" of %s' %
64 (title, name))
65 showavailables(initlevel)
66 errorcnt += 1
67 continue
68 verbose('appropriate section level for "%s %s"' %
69 (mark * (nextlevel * 2), title))
70 curlevel = nextlevel
71
72 return errorcnt
73
74 def checkcmdtable(cmdtable, namefmt, initlevel):
75 errorcnt = 0
76 for k, entry in cmdtable.items():
77 name = k.split("|")[0].lstrip("^")
78 if not entry[0].__doc__:
79 verbose('skip checking %s: no help document' %
80 (namefmt % name))
81 continue
82 errorcnt += checkseclevel(entry[0].__doc__,
83 namefmt % name,
84 initlevel)
85 return errorcnt
86
87 def checkhghelps():
88 errorcnt = 0
89 for names, sec, doc in helptable:
90 if util.safehasattr(doc, '__call__'):
91 doc = doc()
92 errorcnt += checkseclevel(doc,
93 '%s help topic' % names[0],
94 initlevel_topic)
95
96 errorcnt += checkcmdtable(table, '%s command', initlevel_cmd)
97
98 for name in sorted(extensions.enabled().keys() +
99 extensions.disabled().keys()):
100 mod = extensions.load(None, name, None)
101 if not mod.__doc__:
102 verbose('skip checking %s extension: no help document' % name)
103 continue
104 errorcnt += checkseclevel(mod.__doc__,
105 '%s extension' % name,
106 initlevel_ext)
107
108 cmdtable = getattr(mod, 'cmdtable', None)
109 if cmdtable:
110 errorcnt += checkcmdtable(cmdtable,
111 '%s command of ' + name + ' extension',
112 initlevel_ext_cmd)
113 return errorcnt
114
115 def checkfile(filename, initlevel):
116 if filename == '-':
117 filename = 'stdin'
118 doc = sys.stdin.read()
119 else:
120 fp = open(filename)
121 try:
122 doc = fp.read()
123 finally:
124 fp.close()
125
126 verbose('checking input from %s with initlevel %d' %
127 (filename, initlevel))
128 return checkseclevel(doc, 'input from %s' % filename, initlevel)
129
130 if __name__ == "__main__":
131 optparser = optparse.OptionParser("""%prog [options]
132
133 This checks all help documents of Mercurial (topics, commands,
134 extensions and commands of them), if no file is specified by --file
135 option.
136 """)
137 optparser.add_option("-v", "--verbose",
138 help="enable additional output",
139 action="store_true")
140 optparser.add_option("-f", "--file",
141 help="filename to read in (or '-' for stdin)",
142 action="store", default="")
143
144 optparser.add_option("-t", "--topic",
145 help="parse file as help topic",
146 action="store_const", dest="initlevel", const=0)
147 optparser.add_option("-c", "--command",
148 help="parse file as help of core command",
149 action="store_const", dest="initlevel", const=1)
150 optparser.add_option("-e", "--extension",
151 help="parse file as help of extension",
152 action="store_const", dest="initlevel", const=1)
153 optparser.add_option("-C", "--extension-command",
154 help="parse file as help of extension command",
155 action="store_const", dest="initlevel", const=3)
156
157 optparser.add_option("-l", "--initlevel",
158 help="set initial section level manually",
159 action="store", type="int", default=0)
160
161 (options, args) = optparser.parse_args()
162
163 _verbose = options.verbose
164
165 if options.file:
166 if checkfile(options.file, options.initlevel):
167 sys.exit(1)
168 else:
169 if checkhghelps():
170 sys.exit(1)