buildframework/helium/external/python/lib/common/Sphinx-0.5.1-py2.5.egg/sphinx/ext/coverage.py
changeset 179 d8ac696cc51f
equal deleted inserted replaced
1:be27ed110b50 179:d8ac696cc51f
       
     1 # -*- coding: utf-8 -*-
       
     2 """
       
     3     sphinx.ext.coverage
       
     4     ~~~~~~~~~~~~~~~~~~~
       
     5 
       
     6     Check Python modules and C API for coverage.  Mostly written by Josip
       
     7     Dzolonga for the Google Highly Open Participation contest.
       
     8 
       
     9     :copyright: 2008 by Josip Dzolonga, Georg Brandl.
       
    10     :license: BSD.
       
    11 """
       
    12 
       
    13 import re
       
    14 import glob
       
    15 import inspect
       
    16 import cPickle as pickle
       
    17 from os import path
       
    18 
       
    19 from sphinx.builder import Builder
       
    20 
       
    21 
       
    22 # utility
       
    23 def write_header(f, text, char='-'):
       
    24     f.write(text + '\n')
       
    25     f.write(char * len(text) + '\n')
       
    26 
       
    27 def compile_regex_list(name, exps, warnfunc):
       
    28     lst = []
       
    29     for exp in exps:
       
    30         try:
       
    31             lst.append(re.compile(exp))
       
    32         except Exception:
       
    33             warnfunc('invalid regex %r in %s' % (exp, name))
       
    34     return lst
       
    35 
       
    36 
       
    37 class CoverageBuilder(Builder):
       
    38 
       
    39     name = 'coverage'
       
    40 
       
    41     def init(self):
       
    42         self.c_sourcefiles = []
       
    43         for pattern in self.config.coverage_c_path:
       
    44             pattern = path.join(self.srcdir, pattern)
       
    45             self.c_sourcefiles.extend(glob.glob(pattern))
       
    46 
       
    47         self.c_regexes = []
       
    48         for (name, exp) in self.config.coverage_c_regexes.items():
       
    49             try:
       
    50                 self.c_regexes.append((name, re.compile(exp)))
       
    51             except Exception:
       
    52                 self.warn('invalid regex %r in coverage_c_regexes' % exp)
       
    53 
       
    54         self.c_ignorexps = {}
       
    55         for (name, exps) in self.config.coverage_ignore_c_items.iteritems():
       
    56             self.c_ignorexps[name] = compile_regex_list('coverage_ignore_c_items',
       
    57                                                         exps, self.warn)
       
    58         self.mod_ignorexps = compile_regex_list('coverage_ignore_modules',
       
    59                                                 self.config.coverage_ignore_modules,
       
    60                                                 self.warn)
       
    61         self.cls_ignorexps = compile_regex_list('coverage_ignore_classes',
       
    62                                                 self.config.coverage_ignore_classes,
       
    63                                                 self.warn)
       
    64         self.fun_ignorexps = compile_regex_list('coverage_ignore_functions',
       
    65                                                 self.config.coverage_ignore_functions,
       
    66                                                 self.warn)
       
    67 
       
    68     def get_outdated_docs(self):
       
    69         return 'coverage overview'
       
    70 
       
    71     def write(self, *ignored):
       
    72         self.py_undoc = {}
       
    73         self.build_py_coverage()
       
    74         self.write_py_coverage()
       
    75 
       
    76         self.c_undoc = {}
       
    77         self.build_c_coverage()
       
    78         self.write_c_coverage()
       
    79 
       
    80     def build_c_coverage(self):
       
    81         # Fetch all the info from the header files
       
    82         for filename in self.c_sourcefiles:
       
    83             undoc = []
       
    84             f = open(filename, 'r')
       
    85             try:
       
    86                 for line in f:
       
    87                     for key, regex in self.c_regexes:
       
    88                         match = regex.match(line)
       
    89                         if match:
       
    90                             name = match.groups()[0]
       
    91                             if name not in self.env.descrefs:
       
    92                                 for exp in self.c_ignorexps.get(key, ()):
       
    93                                     if exp.match(name):
       
    94                                         break
       
    95                                 else:
       
    96                                     undoc.append((key, name))
       
    97                             continue
       
    98             finally:
       
    99                 f.close()
       
   100             if undoc:
       
   101                 self.c_undoc[filename] = undoc
       
   102 
       
   103     def write_c_coverage(self):
       
   104         output_file = path.join(self.outdir, 'c.txt')
       
   105         op = open(output_file, 'w')
       
   106         try:
       
   107             write_header(op, 'Undocumented C API elements', '=')
       
   108             op.write('\n')
       
   109 
       
   110             for filename, undoc in self.c_undoc.iteritems():
       
   111                 write_header(op, filename)
       
   112                 for typ, name in undoc:
       
   113                     op.write(' * %-50s [%9s]\n' % (name, typ))
       
   114                 op.write('\n')
       
   115         finally:
       
   116             op.close()
       
   117 
       
   118     def build_py_coverage(self):
       
   119         for mod_name in self.env.modules:
       
   120             ignore = False
       
   121             for exp in self.mod_ignorexps:
       
   122                 if exp.match(mod_name):
       
   123                     ignore = True
       
   124                     break
       
   125             if ignore:
       
   126                 continue
       
   127 
       
   128             try:
       
   129                 mod = __import__(mod_name, fromlist=['foo'])
       
   130             except ImportError, err:
       
   131                 self.warn('module %s could not be imported: %s' % (mod_name, err))
       
   132                 self.py_undoc[mod_name] = {'error': err}
       
   133                 continue
       
   134 
       
   135             funcs = []
       
   136             classes = {}
       
   137 
       
   138             for name, obj in inspect.getmembers(mod):
       
   139                 # diverse module attributes are ignored:
       
   140                 if name[0] == '_':
       
   141                     # begins in an underscore
       
   142                     continue
       
   143                 if not hasattr(obj, '__module__'):
       
   144                     # cannot be attributed to a module
       
   145                     continue
       
   146                 if obj.__module__ != mod_name:
       
   147                     # is not defined in this module
       
   148                     continue
       
   149 
       
   150                 full_name = '%s.%s' % (mod_name, name)
       
   151 
       
   152                 if inspect.isfunction(obj):
       
   153                     if full_name not in self.env.descrefs:
       
   154                         for exp in self.fun_ignorexps:
       
   155                             if exp.match(name):
       
   156                                 break
       
   157                         else:
       
   158                             funcs.append(name)
       
   159                 elif inspect.isclass(obj):
       
   160                     for exp in self.cls_ignorexps:
       
   161                         if exp.match(name):
       
   162                             break
       
   163                     else:
       
   164                         if full_name not in self.env.descrefs:
       
   165                             # not documented at all
       
   166                             classes[name] = []
       
   167                             continue
       
   168 
       
   169                         attrs = []
       
   170 
       
   171                         for attr_name, attr in inspect.getmembers(obj, inspect.ismethod):
       
   172                             if attr_name[0] == '_':
       
   173                                 # starts with an underscore, ignore it
       
   174                                 continue
       
   175 
       
   176                             full_attr_name = '%s.%s' % (full_name, attr_name)
       
   177                             if full_attr_name not in self.env.descrefs:
       
   178                                 attrs.append(attr_name)
       
   179 
       
   180                         if attrs:
       
   181                             # some attributes are undocumented
       
   182                             classes[name] = attrs
       
   183 
       
   184             self.py_undoc[mod_name] = {'funcs': funcs, 'classes': classes}
       
   185 
       
   186     def write_py_coverage(self):
       
   187         output_file = path.join(self.outdir, 'python.txt')
       
   188         op = open(output_file, 'w')
       
   189         failed = []
       
   190         try:
       
   191             write_header(op, 'Undocumented Python objects', '=')
       
   192 
       
   193             keys = self.py_undoc.keys()
       
   194             keys.sort()
       
   195             for name in keys:
       
   196                 undoc = self.py_undoc[name]
       
   197                 if 'error' in undoc:
       
   198                     failed.append((name, undoc['error']))
       
   199                 else:
       
   200                     if not undoc['classes'] and not undoc['funcs']:
       
   201                         continue
       
   202 
       
   203                     write_header(op, name)
       
   204                     if undoc['funcs']:
       
   205                         op.write('Functions:\n')
       
   206                         op.writelines(' * %s\n' % x for x in undoc['funcs'])
       
   207                         op.write('\n')
       
   208                     if undoc['classes']:
       
   209                         op.write('Classes:\n')
       
   210                         for name, methods in undoc['classes'].iteritems():
       
   211                             if not methods:
       
   212                                 op.write(' * %s\n' % name)
       
   213                             else:
       
   214                                 op.write(' * %s -- missing methods:\n' % name)
       
   215                                 op.writelines('   - %s\n' % x for x in methods)
       
   216                         op.write('\n')
       
   217 
       
   218             if failed:
       
   219                 write_header(op, 'Modules that failed to import')
       
   220                 op.writelines(' * %s -- %s\n' % x for x in failed)
       
   221         finally:
       
   222             op.close()
       
   223 
       
   224     def finish(self):
       
   225         # dump the coverage data to a pickle file too
       
   226         picklepath = path.join(self.outdir, 'undoc.pickle')
       
   227         dumpfile = open(picklepath, 'wb')
       
   228         try:
       
   229             pickle.dump((self.py_undoc, self.c_undoc), dumpfile)
       
   230         finally:
       
   231             dumpfile.close()
       
   232 
       
   233 
       
   234 def setup(app):
       
   235     app.add_builder(CoverageBuilder)
       
   236     app.add_config_value('coverage_ignore_modules', [], False)
       
   237     app.add_config_value('coverage_ignore_functions', [], False)
       
   238     app.add_config_value('coverage_ignore_classes', [], False)
       
   239     app.add_config_value('coverage_c_path', [], False)
       
   240     app.add_config_value('coverage_c_regexes', {}, False)
       
   241     app.add_config_value('coverage_ignore_c_items', {}, False)