Package epydoc :: Module checker |
[hide private]
[frames] | no frames]

# Source Code for Module epydoc.checker

      1  #
      2  # objdoc: epydoc documentation completeness checker
      3  # Edward Loper
      4  #
      5  # Created [01/30/01 05:18 PM]
      6  # $Id: checker.py 1366 2006-09-07 15:54:59Z edloper $
      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  ## Imports
     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  # The following methods may be undocumented:
     25  _NO_DOCS = ['__hash__', '__repr__', '__str__', '__cmp__']
     26
     27  # The following methods never need descriptions, authors, or
     28  # versions:
     29  _NO_BASIC = ['__hash__', '__repr__', '__str__', '__cmp__']
     30
     31  # The following methods never need return value descriptions.
     32  _NO_RETURN = ['__init__', '__hash__', '__repr__', '__str__', '__cmp__']
     33
     34  # The following methods don't need parameters documented:
     35  _NO_PARAM = ['__cmp__']
     36
     37 -class DocChecker:
     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      # Types
    124      MODULE = 1
    125      CLASS  = 2
    126      FUNC   = 4
    127      VAR    = 8
    128      #IVAR   = 16
    129      #CVAR   = 32
    130      PARAM  = 64
    131      RETURN = 128
    132      PROPERTY = 256
    133      ALL_T  = 1+2+4+8+16+32+64+128+256
    134
    135      # Checks
    136      TYPE = 256
    137      AUTHOR = 1024
    138      VERSION = 2048
    139      DESCR = 4096
    140      ALL_C = 256+512+1024+2048+4096
    141
    142      # Private/public
    143      PRIVATE = 16384
    144
    145      ALL = ALL_T + ALL_C + PRIVATE
    146
    147 -    def __init__(self, docindex):
    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          # Initialize instance variables
    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
    197 -    def _check(self, checks):
    198          self._checks = checks
    199
    200          # Get the list of objects to check.
    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
    226 -    def _name(self, doc):
    227          name = str(doc.canonical_name)
    228          if isinstance(doc, RoutineDoc): name += '()'
    229          return name
    230
    231 -    def _check_basic(self, doc):
    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
    258 -    def _check_module(self, doc):
    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
    269 -    def _check_class(self, doc):
    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
    280 -    def _check_property(self, doc):
    281          if self._checks & DocChecker.PROPERTY:
    282              self._check_basic(doc)
    283
    284 -    def _check_var(self, doc):
    285          """
    286          Run checks on the variable whose documentation is C{var} and
    287          whose name is C{name}.
    288
    289          @param doc: The documentation for the variable to check.
    290          @type doc: L{APIDoc}
    291          @rtype: C{None}
    292          """
    293          if self._checks & DocChecker.VAR:
    294              if (self._checks & (DocChecker.DESCR|DocChecker.TYPE) and
    295                  doc.descr in (None, UNKNOWN) and
    296                  doc.type_descr in (None, UNKNOWN) and
    297                  doc.docstring in (None, UNKNOWN)):
    298                  self.warning('Undocumented', doc)
    299              else:
    300                  if (self._checks & DocChecker.DESCR and
    301                      doc.descr in (None, UNKNOWN)):
    302                      self.warning('No description', doc)
    303                  if (self._checks & DocChecker.TYPE and
    304                      doc.type_descr in (None, UNKNOWN)):
    305                      self.warning('No type information', doc)
    306
    307 -    def _check_func(self, doc):
    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
    348 -    def warning(self, msg, doc):
    349          self._warnings.setdefault(msg,set()).add(doc)
    350