eric6/DebugClients/Python/coverage/report.py

changeset 7427
362cd1b6f81a
parent 6942
2602857055c5
equal deleted inserted replaced
7426:dc171b1d8261 7427:362cd1b6f81a
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 2 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3 3
4 """Reporter foundation for coverage.py.""" 4 """Reporter foundation for coverage.py."""
5 import sys
5 6
6 import os 7 from coverage import env
7 import warnings
8
9 from coverage.files import prep_patterns, FnmatchMatcher 8 from coverage.files import prep_patterns, FnmatchMatcher
10 from coverage.misc import CoverageException, NoSource, NotPython, isolate_module 9 from coverage.misc import CoverageException, NoSource, NotPython, ensure_dir_for_file, file_be_gone
11
12 os = isolate_module(os)
13 10
14 11
15 class Reporter(object): 12 def render_report(output_path, reporter, morfs):
16 """A base class for all reporters.""" 13 """Run the provided reporter ensuring any required setup and cleanup is done
17 14
18 def __init__(self, coverage, config): 15 At a high level this method ensures the output file is ready to be written to. Then writes the
19 """Create a reporter. 16 report to it. Then closes the file and deletes any garbage created if necessary.
17 """
18 file_to_close = None
19 delete_file = False
20 if output_path:
21 if output_path == '-':
22 outfile = sys.stdout
23 else:
24 # Ensure that the output directory is created; done here
25 # because this report pre-opens the output file.
26 # HTMLReport does this using the Report plumbing because
27 # its task is more complex, being multiple files.
28 ensure_dir_for_file(output_path)
29 open_kwargs = {}
30 if env.PY3:
31 open_kwargs['encoding'] = 'utf8'
32 outfile = open(output_path, "w", **open_kwargs)
33 file_to_close = outfile
34 try:
35 return reporter.report(morfs, outfile=outfile)
36 except CoverageException:
37 delete_file = True
38 raise
39 finally:
40 if file_to_close:
41 file_to_close.close()
42 if delete_file:
43 file_be_gone(output_path)
20 44
21 `coverage` is the coverage instance. `config` is an instance of
22 CoverageConfig, for controlling all sorts of behavior.
23 45
24 """ 46 def get_analysis_to_report(coverage, morfs):
25 self.coverage = coverage 47 """Get the files to report on.
26 self.config = config
27 48
28 # The directory into which to place the report, used by some derived 49 For each morf in `morfs`, if it should be reported on (based on the omit
29 # classes. 50 and include configuration options), yield a pair, the `FileReporter` and
30 self.directory = None 51 `Analysis` for the morf.
31 52
32 # Our method find_file_reporters used to set an attribute that other 53 """
33 # code could read. That's been refactored away, but some third parties 54 file_reporters = coverage._get_file_reporters(morfs)
34 # were using that attribute. We'll continue to support it in a noisy 55 config = coverage.config
35 # way for now.
36 self._file_reporters = []
37 56
38 @property 57 if config.report_include:
39 def file_reporters(self): 58 matcher = FnmatchMatcher(prep_patterns(config.report_include))
40 """Keep .file_reporters working for private-grabbing tools.""" 59 file_reporters = [fr for fr in file_reporters if matcher.match(fr.filename)]
41 warnings.warn(
42 "Report.file_reporters will no longer be available in Coverage.py 4.2",
43 DeprecationWarning,
44 )
45 return self._file_reporters
46 60
47 def find_file_reporters(self, morfs): 61 if config.report_omit:
48 """Find the FileReporters we'll report on. 62 matcher = FnmatchMatcher(prep_patterns(config.report_omit))
63 file_reporters = [fr for fr in file_reporters if not matcher.match(fr.filename)]
49 64
50 `morfs` is a list of modules or file names. 65 if not file_reporters:
66 raise CoverageException("No data to report.")
51 67
52 Returns a list of FileReporters. 68 for fr in sorted(file_reporters):
53 69 try:
54 """ 70 analysis = coverage._analyze(fr)
55 reporters = self.coverage._get_file_reporters(morfs) 71 except NoSource:
56 72 if not config.ignore_errors:
57 if self.config.report_include: 73 raise
58 matcher = FnmatchMatcher(prep_patterns(self.config.report_include)) 74 except NotPython:
59 reporters = [fr for fr in reporters if matcher.match(fr.filename)] 75 # Only report errors for .py files, and only if we didn't
60 76 # explicitly suppress those errors.
61 if self.config.report_omit: 77 # NotPython is only raised by PythonFileReporter, which has a
62 matcher = FnmatchMatcher(prep_patterns(self.config.report_omit)) 78 # should_be_python() method.
63 reporters = [fr for fr in reporters if not matcher.match(fr.filename)] 79 if fr.should_be_python():
64 80 if config.ignore_errors:
65 self._file_reporters = sorted(reporters) 81 msg = "Couldn't parse Python file '{}'".format(fr.filename)
66 return self._file_reporters 82 coverage._warn(msg, slug="couldnt-parse")
67 83 else:
68 def report_files(self, report_fn, morfs, directory=None):
69 """Run a reporting function on a number of morfs.
70
71 `report_fn` is called for each relative morf in `morfs`. It is called
72 as::
73
74 report_fn(file_reporter, analysis)
75
76 where `file_reporter` is the `FileReporter` for the morf, and
77 `analysis` is the `Analysis` for the morf.
78
79 """
80 file_reporters = self.find_file_reporters(morfs)
81
82 if not file_reporters:
83 raise CoverageException("No data to report.")
84
85 self.directory = directory
86 if self.directory and not os.path.exists(self.directory):
87 os.makedirs(self.directory)
88
89 for fr in file_reporters:
90 try:
91 report_fn(fr, self.coverage._analyze(fr))
92 except NoSource:
93 if not self.config.ignore_errors:
94 raise 84 raise
95 except NotPython: 85 else:
96 # Only report errors for .py files, and only if we didn't 86 yield (fr, analysis)
97 # explicitly suppress those errors.
98 # NotPython is only raised by PythonFileReporter, which has a
99 # should_be_python() method.
100 if fr.should_be_python():
101 if self.config.ignore_errors:
102 self.coverage._warn("Could not parse Python file {0}".format(fr.filename))
103 else:
104 raise

eric ide

mercurial