Home | History | Annotate | Download | only in coverage
      1 """Reporter foundation for Coverage."""
      2 
      3 import fnmatch, os
      4 from coverage.codeunit import code_unit_factory
      5 from coverage.misc import CoverageException, NoSource, NotPython
      6 
      7 class Reporter(object):
      8     """A base class for all reporters."""
      9 
     10     def __init__(self, coverage, ignore_errors=False):
     11         """Create a reporter.
     12 
     13         `coverage` is the coverage instance. `ignore_errors` controls how
     14         skittish the reporter will be during file processing.
     15 
     16         """
     17         self.coverage = coverage
     18         self.ignore_errors = ignore_errors
     19 
     20         # The code units to report on.  Set by find_code_units.
     21         self.code_units = []
     22 
     23         # The directory into which to place the report, used by some derived
     24         # classes.
     25         self.directory = None
     26 
     27     def find_code_units(self, morfs, config):
     28         """Find the code units we'll report on.
     29 
     30         `morfs` is a list of modules or filenames. `config` is a
     31         CoverageConfig instance.
     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 config.include:
     39             patterns = [file_locator.abs_file(p) for p in 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 config.omit:
     49             patterns = [file_locator.abs_file(p) for p in 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, config, 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         `config` is a CoverageConfig instance.
     73 
     74         """
     75         self.find_code_units(morfs, config)
     76 
     77         if not self.code_units:
     78             raise CoverageException("No data to report.")
     79 
     80         self.directory = directory
     81         if self.directory and not os.path.exists(self.directory):
     82             os.makedirs(self.directory)
     83 
     84         for cu in self.code_units:
     85             try:
     86                 report_fn(cu, self.coverage._analyze(cu))
     87             except (NoSource, NotPython):
     88                 if not self.ignore_errors:
     89                     raise
     90