17757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch"""Reporter foundation for Coverage.""" 27757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 37757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport fnmatch, os 47757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochfrom coverage.codeunit import code_unit_factory 57757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochfrom coverage.misc import CoverageException, NoSource, NotPython 67757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 77757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass Reporter(object): 87757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """A base class for all reporters.""" 97757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def __init__(self, coverage, ignore_errors=False): 117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Create a reporter. 127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch `coverage` is the coverage instance. `ignore_errors` controls how 147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch skittish the reporter will be during file processing. 157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.coverage = coverage 187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.ignore_errors = ignore_errors 197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # The code units to report on. Set by find_code_units. 217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.code_units = [] 227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # The directory into which to place the report, used by some derived 247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # classes. 257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.directory = None 267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def find_code_units(self, morfs, config): 287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Find the code units we'll report on. 297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch `morfs` is a list of modules or filenames. `config` is a 317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch CoverageConfig instance. 327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch morfs = morfs or self.coverage.data.measured_files() 357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch file_locator = self.coverage.file_locator 367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.code_units = code_unit_factory(morfs, file_locator) 377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if config.include: 397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch patterns = [file_locator.abs_file(p) for p in config.include] 407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch filtered = [] 417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for cu in self.code_units: 427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for pattern in patterns: 437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if fnmatch.fnmatch(cu.filename, pattern): 447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch filtered.append(cu) 457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch break 467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.code_units = filtered 477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if config.omit: 497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch patterns = [file_locator.abs_file(p) for p in config.omit] 507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch filtered = [] 517757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for cu in self.code_units: 527757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for pattern in patterns: 537757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if fnmatch.fnmatch(cu.filename, pattern): 547757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch break 557757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch else: 567757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch filtered.append(cu) 577757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.code_units = filtered 587757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 597757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.code_units.sort() 607757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 617757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def report_files(self, report_fn, morfs, config, directory=None): 627757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """Run a reporting function on a number of morfs. 637757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 647757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch `report_fn` is called for each relative morf in `morfs`. It is called 657757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch as:: 667757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 677757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch report_fn(code_unit, analysis) 687757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch where `code_unit` is the `CodeUnit` for the morf, and `analysis` is 707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch the `Analysis` for the morf. 717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch `config` is a CoverageConfig instance. 737757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch """ 757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.find_code_units(morfs, config) 767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 777757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not self.code_units: 787757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch raise CoverageException("No data to report.") 797757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 807757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self.directory = directory 817757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if self.directory and not os.path.exists(self.directory): 827757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch os.makedirs(self.directory) 837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for cu in self.code_units: 857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch try: 867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch report_fn(cu, self.coverage._analyze(cu)) 877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch except (NoSource, NotPython): 887757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not self.ignore_errors: 897757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch raise 90