diff -r dc171b1d8261 -r 362cd1b6f81a eric6/DebugClients/Python/coverage/report.py --- a/eric6/DebugClients/Python/coverage/report.py Wed Feb 19 19:38:36 2020 +0100 +++ b/eric6/DebugClients/Python/coverage/report.py Sat Feb 22 14:27:42 2020 +0100 @@ -1,104 +1,86 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 -# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt +# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt """Reporter foundation for coverage.py.""" - -import os -import warnings +import sys +from coverage import env from coverage.files import prep_patterns, FnmatchMatcher -from coverage.misc import CoverageException, NoSource, NotPython, isolate_module - -os = isolate_module(os) +from coverage.misc import CoverageException, NoSource, NotPython, ensure_dir_for_file, file_be_gone -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 +def render_report(output_path, reporter, morfs): + """Run the provided reporter ensuring any required setup and cleanup is done - # 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 = [] + At a high level this method ensures the output file is ready to be written to. Then writes the + report to it. Then closes the file and deletes any garbage created if necessary. + """ + file_to_close = None + delete_file = False + if output_path: + if output_path == '-': + outfile = sys.stdout + else: + # Ensure that the output directory is created; done here + # because this report pre-opens the output file. + # HTMLReport does this using the Report plumbing because + # its task is more complex, being multiple files. + ensure_dir_for_file(output_path) + open_kwargs = {} + if env.PY3: + open_kwargs['encoding'] = 'utf8' + outfile = open(output_path, "w", **open_kwargs) + file_to_close = outfile + try: + return reporter.report(morfs, outfile=outfile) + except CoverageException: + delete_file = True + raise + finally: + if file_to_close: + file_to_close.close() + if delete_file: + file_be_gone(output_path) - @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) +def get_analysis_to_report(coverage, morfs): + """Get the files to report on. - if self.config.report_include: - matcher = FnmatchMatcher(prep_patterns(self.config.report_include)) - reporters = [fr for fr in reporters if matcher.match(fr.filename)] + For each morf in `morfs`, if it should be reported on (based on the omit + and include configuration options), yield a pair, the `FileReporter` and + `Analysis` for the morf. - 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 + """ + file_reporters = coverage._get_file_reporters(morfs) + config = coverage.config - def report_files(self, report_fn, morfs, directory=None): - """Run a reporting function on a number of morfs. + if config.report_include: + matcher = FnmatchMatcher(prep_patterns(config.report_include)) + file_reporters = [fr for fr in file_reporters if matcher.match(fr.filename)] - `report_fn` is called for each relative morf in `morfs`. It is called - as:: + if config.report_omit: + matcher = FnmatchMatcher(prep_patterns(config.report_omit)) + file_reporters = [fr for fr in file_reporters if not matcher.match(fr.filename)] - report_fn(file_reporter, analysis) - - where `file_reporter` is the `FileReporter` for the morf, and - `analysis` is the `Analysis` for the morf. + if not file_reporters: + raise CoverageException("No data to report.") - """ - 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: + for fr in sorted(file_reporters): + try: + analysis = coverage._analyze(fr) + except NoSource: + if not 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 config.ignore_errors: + msg = "Couldn't parse Python file '{}'".format(fr.filename) + coverage._warn(msg, slug="couldnt-parse") + else: 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 + else: + yield (fr, analysis)