# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """Reporter foundation for coverage.py.""" import os import warnings from coverage.files import prep_patterns, FnmatchMatcher from coverage.misc import CoverageException, NoSource, NotPython, isolate_module os = isolate_module(os) 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 directory into which to place the report, used by some derived # classes. self.directory = None # Our method find_file_reporters used to set an attribute that other # code could read. That's been refactored away, but some third parties # were using that attribute. We'll continue to support it in a noisy # way for now. self._file_reporters = [] @property def file_reporters(self): """Keep .file_reporters working for private-grabbing tools.""" warnings.warn( "Report.file_reporters will no longer be available in Coverage.py 4.2", DeprecationWarning, ) return self._file_reporters def find_file_reporters(self, morfs): """Find the FileReporters we'll report on. `morfs` is a list of modules or file names. Returns a list of FileReporters. """ reporters = self.coverage._get_file_reporters(morfs) if self.config.report_include: matcher = FnmatchMatcher(prep_patterns(self.config.report_include)) reporters = [fr for fr in reporters if matcher.match(fr.filename)] if self.config.report_omit: matcher = FnmatchMatcher(prep_patterns(self.config.report_omit)) reporters = [fr for fr in reporters if not matcher.match(fr.filename)] self._file_reporters = sorted(reporters) return self._file_reporters 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(file_reporter, analysis) where `file_reporter` is the `FileReporter` for the morf, and `analysis` is the `Analysis` for the morf. """ file_reporters = self.find_file_reporters(morfs) if not file_reporters: raise CoverageException("No data to report.") self.directory = directory if self.directory and not os.path.exists(self.directory): os.makedirs(self.directory) for fr in file_reporters: try: report_fn(fr, self.coverage._analyze(fr)) 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. # NotPython is only raised by PythonFileReporter, which has a # should_be_python() method. if fr.should_be_python(): if self.config.ignore_errors: self.coverage._warn("Could not parse Python file {0}".format(fr.filename)) else: raise