"""Reporter foundation for Coverage.""" import fnmatch, os from coverage.codeunit import code_unit_factory from coverage.files import prep_patterns from coverage.misc import CoverageException, NoSource, NotPython class Reporter(object): """A base class for all reporters.""" def __init__(self, coverage, config): """Create a reporter. `coverage` is the coverage instance. `config` is an instance of CoverageConfig, for controlling all sorts of behavior. """ self.coverage = coverage self.config = config # The code units to report on. Set by find_code_units. self.code_units = [] # The directory into which to place the report, used by some derived # classes. self.directory = None def find_code_units(self, morfs): """Find the code units we'll report on. `morfs` is a list of modules or filenames. """ morfs = morfs or self.coverage.data.measured_files() file_locator = self.coverage.file_locator self.code_units = code_unit_factory(morfs, file_locator) if self.config.include: patterns = prep_patterns(self.config.include) filtered = [] for cu in self.code_units: for pattern in patterns: if fnmatch.fnmatch(cu.filename, pattern): filtered.append(cu) break self.code_units = filtered if self.config.omit: patterns = prep_patterns(self.config.omit) filtered = [] for cu in self.code_units: for pattern in patterns: if fnmatch.fnmatch(cu.filename, pattern): break else: filtered.append(cu) self.code_units = filtered self.code_units.sort() def report_files(self, report_fn, morfs, directory=None): """Run a reporting function on a number of morfs. `report_fn` is called for each relative morf in `morfs`. It is called as:: report_fn(code_unit, analysis) where `code_unit` is the `CodeUnit` for the morf, and `analysis` is the `Analysis` for the morf. """ self.find_code_units(morfs) if not self.code_units: raise CoverageException("No data to report.") self.directory = directory if self.directory and not os.path.exists(self.directory): os.makedirs(self.directory) for cu in self.code_units: try: report_fn(cu, self.coverage._analyze(cu)) except NoSource: if not self.config.ignore_errors: raise except NotPython: # Only report errors for .py files, and only if we didn't # explicitly suppress those errors. if cu.should_be_python() and not self.config.ignore_errors: raise