1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
|
#
# objdoc: epydoc documentation completeness checker
# Edward Loper
#
# Created [01/30/01 05:18 PM]
# $Id: checker.py 1366 2006-09-07 15:54:59Z edloper $
#
"""
Documentation completeness checker. This module defines a single
class, C{DocChecker}, which can be used to check the that specified
classes of objects are documented.
"""
__docformat__ = 'epytext en'
##################################################
## Imports
##################################################
import re, sys, os.path, string
from xml.dom.minidom import Text as _Text
from epydoc.apidoc import *
# The following methods may be undocumented:
_NO_DOCS = ['__hash__', '__repr__', '__str__', '__cmp__']
# The following methods never need descriptions, authors, or
# versions:
_NO_BASIC = ['__hash__', '__repr__', '__str__', '__cmp__']
# The following methods never need return value descriptions.
_NO_RETURN = ['__init__', '__hash__', '__repr__', '__str__', '__cmp__']
# The following methods don't need parameters documented:
_NO_PARAM = ['__cmp__']
class DocChecker:
"""
Documentation completeness checker. C{DocChecker} can be used to
check that specified classes of objects are documented. To check
the documentation for a group of objects, you should create a
C{DocChecker} from a L{DocIndex<apidoc.DocIndex>} that documents
those objects; and then use the L{check} method to run specified
checks on the objects' documentation.
What checks are run, and what objects they are run on, are
specified by the constants defined by C{DocChecker}. These
constants are divided into three groups.
- Type specifiers indicate what type of objects should be
checked: L{MODULE}; L{CLASS}; L{FUNC}; L{VAR}; L{IVAR};
L{CVAR}; L{PARAM}; and L{RETURN}.
- Public/private specifiers indicate whether public or private
objects should be checked: L{PRIVATE}.
- Check specifiers indicate what checks should be run on the
objects: L{TYPE}; L{DESCR}; L{AUTHOR};
and L{VERSION}.
The L{check} method is used to perform a check on the
documentation. Its parameter is formed by or-ing together at
least one value from each specifier group:
>>> checker.check(DocChecker.MODULE | DocChecker.DESCR)
To specify multiple values from a single group, simply or their
values together:
>>> checker.check(DocChecker.MODULE | DocChecker.CLASS |
... DocChecker.FUNC )
@group Types: MODULE, CLASS, FUNC, VAR, IVAR, CVAR, PARAM,
RETURN, ALL_T
@type MODULE: C{int}
@cvar MODULE: Type specifier that indicates that the documentation
of modules should be checked.
@type CLASS: C{int}
@cvar CLASS: Type specifier that indicates that the documentation
of classes should be checked.
@type FUNC: C{int}
@cvar FUNC: Type specifier that indicates that the documentation
of functions should be checked.
@type VAR: C{int}
@cvar VAR: Type specifier that indicates that the documentation
of module variables should be checked.
@type IVAR: C{int}
@cvar IVAR: Type specifier that indicates that the documentation
of instance variables should be checked.
@type CVAR: C{int}
@cvar CVAR: Type specifier that indicates that the documentation
of class variables should be checked.
@type PARAM: C{int}
@cvar PARAM: Type specifier that indicates that the documentation
of function and method parameters should be checked.
@type RETURN: C{int}
@cvar RETURN: Type specifier that indicates that the documentation
of return values should be checked.
@type ALL_T: C{int}
@cvar ALL_T: Type specifier that indicates that the documentation
of all objects should be checked.
@group Checks: TYPE, AUTHOR, VERSION, DESCR, ALL_C
@type TYPE: C{int}
@cvar TYPE: Check specifier that indicates that every variable and
parameter should have a C{@type} field.
@type AUTHOR: C{int}
@cvar AUTHOR: Check specifier that indicates that every object
should have an C{author} field.
@type VERSION: C{int}
@cvar VERSION: Check specifier that indicates that every object
should have a C{version} field.
@type DESCR: C{int}
@cvar DESCR: Check specifier that indicates that every object
should have a description.
@type ALL_C: C{int}
@cvar ALL_C: Check specifier that indicates that all checks
should be run.
@group Publicity: PRIVATE
@type PRIVATE: C{int}
@cvar PRIVATE: Specifier that indicates that private objects should
be checked.
"""
# Types
MODULE = 1
CLASS = 2
FUNC = 4
VAR = 8
#IVAR = 16
#CVAR = 32
PARAM = 64
RETURN = 128
PROPERTY = 256
ALL_T = 1+2+4+8+16+32+64+128+256
# Checks
TYPE = 256
AUTHOR = 1024
VERSION = 2048
DESCR = 4096
ALL_C = 256+512+1024+2048+4096
# Private/public
PRIVATE = 16384
ALL = ALL_T + ALL_C + PRIVATE
def __init__(self, docindex):
"""
Create a new C{DocChecker} that can be used to run checks on
the documentation of the objects documented by C{docindex}
@param docindex: A documentation map containing the
documentation for the objects to be checked.
@type docindex: L{Docindex<apidoc.DocIndex>}
"""
self._docindex = docindex
# Initialize instance variables
self._checks = 0
self._last_warn = None
self._out = sys.stdout
self._num_warnings = 0
def check(self, *check_sets):
"""
Run the specified checks on the documentation of the objects
contained by this C{DocChecker}'s C{DocIndex}. Any errors found
are printed to standard out.
@param check_sets: The checks that should be run on the
documentation. This value is constructed by or-ing
together the specifiers that indicate which objects should
be checked, and which checks should be run. See the
L{module description<checker>} for more information.
If no checks are specified, then a default set of checks
will be run.
@type check_sets: C{int}
@return: True if no problems were found.
@rtype: C{boolean}
"""
if not check_sets:
check_sets = (DocChecker.MODULE | DocChecker.CLASS |
DocChecker.FUNC | DocChecker.VAR |
DocChecker.DESCR,)
self._warnings = {}
log.start_progress('Checking docs')
for j, checks in enumerate(check_sets):
self._check(checks)
log.end_progress()
for (warning, docs) in self._warnings.items():
docs = sorted(docs)
docnames = '\n'.join([' - %s' % self._name(d) for d in docs])
log.warning('%s:\n%s' % (warning, docnames))
def _check(self, checks):
self._checks = checks
# Get the list of objects to check.
valdocs = sorted(self._docindex.reachable_valdocs(
imports=False, packages=False, bases=False, submodules=False,
subclasses=False, private = (checks & DocChecker.PRIVATE)))
docs = set()
for d in valdocs:
if not isinstance(d, GenericValueDoc): docs.add(d)
for doc in valdocs:
if isinstance(doc, NamespaceDoc):
for d in doc.variables.values():
if isinstance(d.value, GenericValueDoc): docs.add(d)
for i, doc in enumerate(sorted(docs)):
if isinstance(doc, ModuleDoc):
self._check_module(doc)
elif isinstance(doc, ClassDoc):
self._check_class(doc)
elif isinstance(doc, RoutineDoc):
self._check_func(doc)
elif isinstance(doc, PropertyDoc):
self._check_property(doc)
elif isinstance(doc, VariableDoc):
self._check_var(doc)
else:
log.error("Don't know how to check %r" % doc)
def _name(self, doc):
name = str(doc.canonical_name)
if isinstance(doc, RoutineDoc): name += '()'
return name
def _check_basic(self, doc):
"""
Check the description, author, version, and see-also fields of
C{doc}. This is used as a helper function by L{_check_module},
L{_check_class}, and L{_check_func}.
@param doc: The documentation that should be checked.
@type doc: L{APIDoc}
@rtype: C{None}
"""
if ((self._checks & DocChecker.DESCR) and
(doc.descr in (None, UNKNOWN))):
if doc.docstring in (None, UNKNOWN):
self.warning('Undocumented', doc)
else:
self.warning('No description', doc)
if self._checks & DocChecker.AUTHOR:
for tag, arg, descr in doc.metadata:
if 'author' == tag: break
else:
self.warning('No authors', doc)
if self._checks & DocChecker.VERSION:
for tag, arg, descr in doc.metadata:
if 'version' == tag: break
else:
self.warning('No version', doc)
def _check_module(self, doc):
"""
Run checks on the module whose APIDoc is C{doc}.
@param doc: The APIDoc of the module to check.
@type doc: L{APIDoc}
@rtype: C{None}
"""
if self._checks & DocChecker.MODULE:
self._check_basic(doc)
def _check_class(self, doc):
"""
Run checks on the class whose APIDoc is C{doc}.
@param doc: The APIDoc of the class to check.
@type doc: L{APIDoc}
@rtype: C{None}
"""
if self._checks & DocChecker.CLASS:
self._check_basic(doc)
def _check_property(self, doc):
if self._checks & DocChecker.PROPERTY:
self._check_basic(doc)
def _check_var(self, doc):
"""
Run checks on the variable whose documentation is C{var} and
whose name is C{name}.
@param doc: The documentation for the variable to check.
@type doc: L{APIDoc}
@rtype: C{None}
"""
if self._checks & DocChecker.VAR:
if (self._checks & (DocChecker.DESCR|DocChecker.TYPE) and
doc.descr in (None, UNKNOWN) and
doc.type_descr in (None, UNKNOWN) and
doc.docstring in (None, UNKNOWN)):
self.warning('Undocumented', doc)
else:
if (self._checks & DocChecker.DESCR and
doc.descr in (None, UNKNOWN)):
self.warning('No description', doc)
if (self._checks & DocChecker.TYPE and
doc.type_descr in (None, UNKNOWN)):
self.warning('No type information', doc)
def _check_func(self, doc):
"""
Run checks on the function whose APIDoc is C{doc}.
@param doc: The APIDoc of the function to check.
@type doc: L{APIDoc}
@rtype: C{None}
"""
name = doc.canonical_name
if (self._checks & DocChecker.FUNC and
doc.docstring in (None, UNKNOWN) and
doc.canonical_name[-1] not in _NO_DOCS):
self.warning('Undocumented', doc)
return
if (self._checks & DocChecker.FUNC and
doc.canonical_name[-1] not in _NO_BASIC):
self._check_basic(doc)
if (self._checks & DocChecker.RETURN and
doc.canonical_name[-1] not in _NO_RETURN):
if (doc.return_type in (None, UNKNOWN) and
doc.return_descr in (None, UNKNOWN)):
self.warning('No return descr', doc)
if (self._checks & DocChecker.PARAM and
doc.canonical_name[-1] not in _NO_PARAM):
if doc.arg_descrs in (None, UNKNOWN):
self.warning('No argument info', doc)
else:
args_with_descr = []
for arg, descr in doc.arg_descrs:
if isinstance(arg, basestring):
args_with_descr.append(arg)
else:
args_with_descr += arg
for posarg in doc.posargs:
if (self._checks & DocChecker.DESCR and
posarg not in args_with_descr):
self.warning('Argument(s) not described', doc)
if (self._checks & DocChecker.TYPE and
posarg not in doc.arg_types):
self.warning('Argument type(s) not described', doc)
def warning(self, msg, doc):
self._warnings.setdefault(msg,set()).add(doc)
|