/documentor/libraries/Sphinx-1.1.3-py3.2/sphinx/ext/coverage.py
Python | 265 lines | 207 code | 36 blank | 22 comment | 76 complexity | a7b1bdc61e55b3f156fbf04bff538855 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-3.0, GPL-2.0
- # -*- coding: utf-8 -*-
- """
- sphinx.ext.coverage
- ~~~~~~~~~~~~~~~~~~~
- Check Python modules and C API for coverage. Mostly written by Josip
- Dzolonga for the Google Highly Open Participation contest.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
- """
- import re
- import glob
- import inspect
- import pickle as pickle
- from os import path
- from sphinx.builders import Builder
- # utility
- def write_header(f, text, char='-'):
- f.write(text + '\n')
- f.write(char * len(text) + '\n')
- def compile_regex_list(name, exps, warnfunc):
- lst = []
- for exp in exps:
- try:
- lst.append(re.compile(exp))
- except Exception:
- warnfunc('invalid regex %r in %s' % (exp, name))
- return lst
- class CoverageBuilder(Builder):
- name = 'coverage'
- def init(self):
- self.c_sourcefiles = []
- for pattern in self.config.coverage_c_path:
- pattern = path.join(self.srcdir, pattern)
- self.c_sourcefiles.extend(glob.glob(pattern))
- self.c_regexes = []
- for (name, exp) in list(self.config.coverage_c_regexes.items()):
- try:
- self.c_regexes.append((name, re.compile(exp)))
- except Exception:
- self.warn('invalid regex %r in coverage_c_regexes' % exp)
- self.c_ignorexps = {}
- for (name, exps) in self.config.coverage_ignore_c_items.items():
- self.c_ignorexps[name] = compile_regex_list(
- 'coverage_ignore_c_items', exps, self.warn)
- self.mod_ignorexps = compile_regex_list(
- 'coverage_ignore_modules', self.config.coverage_ignore_modules,
- self.warn)
- self.cls_ignorexps = compile_regex_list(
- 'coverage_ignore_classes', self.config.coverage_ignore_classes,
- self.warn)
- self.fun_ignorexps = compile_regex_list(
- 'coverage_ignore_functions', self.config.coverage_ignore_functions,
- self.warn)
- def get_outdated_docs(self):
- return 'coverage overview'
- def write(self, *ignored):
- self.py_undoc = {}
- self.build_py_coverage()
- self.write_py_coverage()
- self.c_undoc = {}
- self.build_c_coverage()
- self.write_c_coverage()
- def build_c_coverage(self):
- # Fetch all the info from the header files
- c_objects = self.env.domaindata['c']['objects']
- for filename in self.c_sourcefiles:
- undoc = []
- f = open(filename, 'r')
- try:
- for line in f:
- for key, regex in self.c_regexes:
- match = regex.match(line)
- if match:
- name = match.groups()[0]
- if name not in c_objects:
- for exp in self.c_ignorexps.get(key, ()):
- if exp.match(name):
- break
- else:
- undoc.append((key, name))
- continue
- finally:
- f.close()
- if undoc:
- self.c_undoc[filename] = undoc
- def write_c_coverage(self):
- output_file = path.join(self.outdir, 'c.txt')
- op = open(output_file, 'w')
- try:
- if self.config.coverage_write_headline:
- write_header(op, 'Undocumented C API elements', '=')
- op.write('\n')
- for filename, undoc in self.c_undoc.items():
- write_header(op, filename)
- for typ, name in undoc:
- op.write(' * %-50s [%9s]\n' % (name, typ))
- op.write('\n')
- finally:
- op.close()
- def build_py_coverage(self):
- objects = self.env.domaindata['py']['objects']
- modules = self.env.domaindata['py']['modules']
- skip_undoc = self.config.coverage_skip_undoc_in_source
- for mod_name in modules:
- ignore = False
- for exp in self.mod_ignorexps:
- if exp.match(mod_name):
- ignore = True
- break
- if ignore:
- continue
- try:
- mod = __import__(mod_name, fromlist=['foo'])
- except ImportError as err:
- self.warn('module %s could not be imported: %s' %
- (mod_name, err))
- self.py_undoc[mod_name] = {'error': err}
- continue
- funcs = []
- classes = {}
- for name, obj in inspect.getmembers(mod):
- # diverse module attributes are ignored:
- if name[0] == '_':
- # begins in an underscore
- continue
- if not hasattr(obj, '__module__'):
- # cannot be attributed to a module
- continue
- if obj.__module__ != mod_name:
- # is not defined in this module
- continue
- full_name = '%s.%s' % (mod_name, name)
- if inspect.isfunction(obj):
- if full_name not in objects:
- for exp in self.fun_ignorexps:
- if exp.match(name):
- break
- else:
- if skip_undoc and not obj.__doc__:
- continue
- funcs.append(name)
- elif inspect.isclass(obj):
- for exp in self.cls_ignorexps:
- if exp.match(name):
- break
- else:
- if full_name not in objects:
- if skip_undoc and not obj.__doc__:
- continue
- # not documented at all
- classes[name] = []
- continue
- attrs = []
- for attr_name in dir(obj):
- if attr_name not in obj.__dict__:
- continue
- attr = getattr(obj, attr_name)
- if not (inspect.ismethod(attr) or
- inspect.isfunction(attr)):
- continue
- if attr_name[0] == '_':
- # starts with an underscore, ignore it
- continue
- if skip_undoc and not attr.__doc__:
- # skip methods without docstring if wished
- continue
- full_attr_name = '%s.%s' % (full_name, attr_name)
- if full_attr_name not in objects:
- attrs.append(attr_name)
- if attrs:
- # some attributes are undocumented
- classes[name] = attrs
- self.py_undoc[mod_name] = {'funcs': funcs, 'classes': classes}
- def write_py_coverage(self):
- output_file = path.join(self.outdir, 'python.txt')
- op = open(output_file, 'w')
- failed = []
- try:
- if self.config.coverage_write_headline:
- write_header(op, 'Undocumented Python objects', '=')
- keys = list(self.py_undoc.keys())
- keys.sort()
- for name in keys:
- undoc = self.py_undoc[name]
- if 'error' in undoc:
- failed.append((name, undoc['error']))
- else:
- if not undoc['classes'] and not undoc['funcs']:
- continue
- write_header(op, name)
- if undoc['funcs']:
- op.write('Functions:\n')
- op.writelines(' * %s\n' % x for x in undoc['funcs'])
- op.write('\n')
- if undoc['classes']:
- op.write('Classes:\n')
- for name, methods in sorted(
- undoc['classes'].items()):
- if not methods:
- op.write(' * %s\n' % name)
- else:
- op.write(' * %s -- missing methods:\n\n' % name)
- op.writelines(' - %s\n' % x for x in methods)
- op.write('\n')
- if failed:
- write_header(op, 'Modules that failed to import')
- op.writelines(' * %s -- %s\n' % x for x in failed)
- finally:
- op.close()
- def finish(self):
- # dump the coverage data to a pickle file too
- picklepath = path.join(self.outdir, 'undoc.pickle')
- dumpfile = open(picklepath, 'wb')
- try:
- pickle.dump((self.py_undoc, self.c_undoc), dumpfile)
- finally:
- dumpfile.close()
- def setup(app):
- app.add_builder(CoverageBuilder)
- app.add_config_value('coverage_ignore_modules', [], False)
- app.add_config_value('coverage_ignore_functions', [], False)
- app.add_config_value('coverage_ignore_classes', [], False)
- app.add_config_value('coverage_c_path', [], False)
- app.add_config_value('coverage_c_regexes', {}, False)
- app.add_config_value('coverage_ignore_c_items', {}, False)
- app.add_config_value('coverage_write_headline', True, False)
- app.add_config_value('coverage_skip_undoc_in_source', False, False)