1"""Reporter foundation for Coverage.""" 2 3import fnmatch, os 4from coverage.codeunit import code_unit_factory 5from coverage.files import prep_patterns 6from coverage.misc import CoverageException, NoSource, NotPython 7 8class Reporter(object): 9 """A base class for all reporters.""" 10 11 def __init__(self, coverage, config): 12 """Create a reporter. 13 14 `coverage` is the coverage instance. `config` is an instance of 15 CoverageConfig, for controlling all sorts of behavior. 16 17 """ 18 self.coverage = coverage 19 self.config = config 20 21 # The code units to report on. Set by find_code_units. 22 self.code_units = [] 23 24 # The directory into which to place the report, used by some derived 25 # classes. 26 self.directory = None 27 28 def find_code_units(self, morfs): 29 """Find the code units we'll report on. 30 31 `morfs` is a list of modules or filenames. 32 33 """ 34 morfs = morfs or self.coverage.data.measured_files() 35 file_locator = self.coverage.file_locator 36 self.code_units = code_unit_factory(morfs, file_locator) 37 38 if self.config.include: 39 patterns = prep_patterns(self.config.include) 40 filtered = [] 41 for cu in self.code_units: 42 for pattern in patterns: 43 if fnmatch.fnmatch(cu.filename, pattern): 44 filtered.append(cu) 45 break 46 self.code_units = filtered 47 48 if self.config.omit: 49 patterns = prep_patterns(self.config.omit) 50 filtered = [] 51 for cu in self.code_units: 52 for pattern in patterns: 53 if fnmatch.fnmatch(cu.filename, pattern): 54 break 55 else: 56 filtered.append(cu) 57 self.code_units = filtered 58 59 self.code_units.sort() 60 61 def report_files(self, report_fn, morfs, directory=None): 62 """Run a reporting function on a number of morfs. 63 64 `report_fn` is called for each relative morf in `morfs`. It is called 65 as:: 66 67 report_fn(code_unit, analysis) 68 69 where `code_unit` is the `CodeUnit` for the morf, and `analysis` is 70 the `Analysis` for the morf. 71 72 """ 73 self.find_code_units(morfs) 74 75 if not self.code_units: 76 raise CoverageException("No data to report.") 77 78 self.directory = directory 79 if self.directory and not os.path.exists(self.directory): 80 os.makedirs(self.directory) 81 82 for cu in self.code_units: 83 try: 84 report_fn(cu, self.coverage._analyze(cu)) 85 except NoSource: 86 if not self.config.ignore_errors: 87 raise 88 except NotPython: 89 # Only report errors for .py files, and only if we didn't 90 # explicitly suppress those errors. 91 if cu.should_be_python() and not self.config.ignore_errors: 92 raise 93