eric6/DebugClients/Python/coverage/report.py

changeset 7427
362cd1b6f81a
parent 6942
2602857055c5
--- 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)

eric ide

mercurial