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