eric6/DebugClients/Python/coverage/summary.py

changeset 7427
362cd1b6f81a
parent 6942
2602857055c5
child 7702
f8b97639deb5
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 """Summary reporting""" 4 """Summary reporting"""
5 5
6 import sys 6 import sys
7 7
8 from coverage import env 8 from coverage import env
9 from coverage.report import Reporter 9 from coverage.report import get_analysis_to_report
10 from coverage.results import Numbers 10 from coverage.results import Numbers
11 from coverage.misc import NotPython, CoverageException, output_encoding, StopEverything 11 from coverage.misc import NotPython, CoverageException, output_encoding
12 12
13 13
14 class SummaryReporter(Reporter): 14 class SummaryReporter(object):
15 """A reporter for writing the summary report.""" 15 """A reporter for writing the summary report."""
16 16
17 def __init__(self, coverage, config): 17 def __init__(self, coverage):
18 super(SummaryReporter, self).__init__(coverage, config) 18 self.coverage = coverage
19 self.branches = coverage.data.has_arcs() 19 self.config = self.coverage.config
20 self.branches = coverage.get_data().has_arcs()
21 self.outfile = None
22 self.fr_analysis = []
23 self.skipped_count = 0
24 self.empty_count = 0
25 self.total = Numbers()
26 self.fmt_err = u"%s %s: %s"
27
28 def writeout(self, line):
29 """Write a line to the output, adding a newline."""
30 if env.PY2:
31 line = line.encode(output_encoding())
32 self.outfile.write(line.rstrip())
33 self.outfile.write("\n")
20 34
21 def report(self, morfs, outfile=None): 35 def report(self, morfs, outfile=None):
22 """Writes a report summarizing coverage statistics per module. 36 """Writes a report summarizing coverage statistics per module.
23 37
24 `outfile` is a file object to write the summary to. It must be opened 38 `outfile` is a file object to write the summary to. It must be opened
25 for native strings (bytes on Python 2, Unicode on Python 3). 39 for native strings (bytes on Python 2, Unicode on Python 3).
26 40
27 """ 41 """
28 if outfile is None: 42 self.outfile = outfile or sys.stdout
29 outfile = sys.stdout
30 43
31 def writeout(line): 44 self.coverage.get_data().set_query_contexts(self.config.report_contexts)
32 """Write a line to the output, adding a newline.""" 45 for fr, analysis in get_analysis_to_report(self.coverage, morfs):
33 if env.PY2: 46 self.report_one_file(fr, analysis)
34 line = line.encode(output_encoding())
35 outfile.write(line.rstrip())
36 outfile.write("\n")
37
38 fr_analysis = []
39 skipped_count = 0
40 total = Numbers()
41
42 fmt_err = u"%s %s: %s"
43
44 for fr in self.find_file_reporters(morfs):
45 try:
46 analysis = self.coverage._analyze(fr)
47 nums = analysis.numbers
48 total += nums
49
50 if self.config.skip_covered:
51 # Don't report on 100% files.
52 no_missing_lines = (nums.n_missing == 0)
53 no_missing_branches = (nums.n_partial_branches == 0)
54 if no_missing_lines and no_missing_branches:
55 skipped_count += 1
56 continue
57 fr_analysis.append((fr, analysis))
58 except StopEverything:
59 # Don't report this on single files, it's a systemic problem.
60 raise
61 except Exception:
62 report_it = not self.config.ignore_errors
63 if report_it:
64 typ, msg = sys.exc_info()[:2]
65 # NotPython is only raised by PythonFileReporter, which has a
66 # should_be_python() method.
67 if issubclass(typ, NotPython) and not fr.should_be_python():
68 report_it = False
69 if report_it:
70 writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg))
71 47
72 # Prepare the formatting strings, header, and column sorting. 48 # Prepare the formatting strings, header, and column sorting.
73 max_name = max([len(fr.relative_filename()) for (fr, analysis) in fr_analysis] + [5]) 49 max_name = max([len(fr.relative_filename()) for (fr, analysis) in self.fr_analysis] + [5])
74 fmt_name = u"%%- %ds " % max_name 50 fmt_name = u"%%- %ds " % max_name
75 fmt_skip_covered = u"\n%s file%s skipped due to complete coverage." 51 fmt_skip_covered = u"\n%s file%s skipped due to complete coverage."
52 fmt_skip_empty = u"\n%s empty file%s skipped."
76 53
77 header = (fmt_name % "Name") + u" Stmts Miss" 54 header = (fmt_name % "Name") + u" Stmts Miss"
78 fmt_coverage = fmt_name + u"%6d %6d" 55 fmt_coverage = fmt_name + u"%6d %6d"
79 if self.branches: 56 if self.branches:
80 header += u" Branch BrPart" 57 header += u" Branch BrPart"
90 column_order = dict(name=0, stmts=1, miss=2, cover=-1) 67 column_order = dict(name=0, stmts=1, miss=2, cover=-1)
91 if self.branches: 68 if self.branches:
92 column_order.update(dict(branch=3, brpart=4)) 69 column_order.update(dict(branch=3, brpart=4))
93 70
94 # Write the header 71 # Write the header
95 writeout(header) 72 self.writeout(header)
96 writeout(rule) 73 self.writeout(rule)
97 74
98 # `lines` is a list of pairs, (line text, line values). The line text 75 # `lines` is a list of pairs, (line text, line values). The line text
99 # is a string that will be printed, and line values is a tuple of 76 # is a string that will be printed, and line values is a tuple of
100 # sortable values. 77 # sortable values.
101 lines = [] 78 lines = []
102 79
103 for (fr, analysis) in fr_analysis: 80 for (fr, analysis) in self.fr_analysis:
104 try: 81 try:
105 nums = analysis.numbers 82 nums = analysis.numbers
106 83
107 args = (fr.relative_filename(), nums.n_statements, nums.n_missing) 84 args = (fr.relative_filename(), nums.n_statements, nums.n_missing)
108 if self.branches: 85 if self.branches:
109 args += (nums.n_branches, nums.n_partial_branches) 86 args += (nums.n_branches, nums.n_partial_branches)
110 args += (nums.pc_covered_str,) 87 args += (nums.pc_covered_str,)
111 if self.config.show_missing: 88 if self.config.show_missing:
112 missing_fmtd = analysis.missing_formatted() 89 args += (analysis.missing_formatted(branches=True),)
113 if self.branches:
114 branches_fmtd = analysis.arcs_missing_formatted()
115 if branches_fmtd:
116 if missing_fmtd:
117 missing_fmtd += ", "
118 missing_fmtd += branches_fmtd
119 args += (missing_fmtd,)
120 text = fmt_coverage % args 90 text = fmt_coverage % args
121 # Add numeric percent coverage so that sorting makes sense. 91 # Add numeric percent coverage so that sorting makes sense.
122 args += (nums.pc_covered,) 92 args += (nums.pc_covered,)
123 lines.append((text, args)) 93 lines.append((text, args))
124 except Exception: 94 except Exception:
128 # NotPython is only raised by PythonFileReporter, which has a 98 # NotPython is only raised by PythonFileReporter, which has a
129 # should_be_python() method. 99 # should_be_python() method.
130 if typ is NotPython and not fr.should_be_python(): 100 if typ is NotPython and not fr.should_be_python():
131 report_it = False 101 report_it = False
132 if report_it: 102 if report_it:
133 writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg)) 103 self.writeout(self.fmt_err % (fr.relative_filename(), typ.__name__, msg))
134 104
135 # Sort the lines and write them out. 105 # Sort the lines and write them out.
136 if getattr(self.config, 'sort', None): 106 if getattr(self.config, 'sort', None):
137 position = column_order.get(self.config.sort.lower()) 107 position = column_order.get(self.config.sort.lower())
138 if position is None: 108 if position is None:
139 raise CoverageException("Invalid sorting option: {0!r}".format(self.config.sort)) 109 raise CoverageException("Invalid sorting option: {!r}".format(self.config.sort))
140 lines.sort(key=lambda l: (l[1][position], l[0])) 110 lines.sort(key=lambda l: (l[1][position], l[0]))
141 111
142 for line in lines: 112 for line in lines:
143 writeout(line[0]) 113 self.writeout(line[0])
144 114
145 # Write a TOTAl line if we had more than one file. 115 # Write a TOTAl line if we had more than one file.
146 if total.n_files > 1: 116 if self.total.n_files > 1:
147 writeout(rule) 117 self.writeout(rule)
148 args = ("TOTAL", total.n_statements, total.n_missing) 118 args = ("TOTAL", self.total.n_statements, self.total.n_missing)
149 if self.branches: 119 if self.branches:
150 args += (total.n_branches, total.n_partial_branches) 120 args += (self.total.n_branches, self.total.n_partial_branches)
151 args += (total.pc_covered_str,) 121 args += (self.total.pc_covered_str,)
152 if self.config.show_missing: 122 if self.config.show_missing:
153 args += ("",) 123 args += ("",)
154 writeout(fmt_coverage % args) 124 self.writeout(fmt_coverage % args)
155 125
156 # Write other final lines. 126 # Write other final lines.
157 if not total.n_files and not skipped_count: 127 if not self.total.n_files and not self.skipped_count:
158 raise CoverageException("No data to report.") 128 raise CoverageException("No data to report.")
159 129
160 if self.config.skip_covered and skipped_count: 130 if self.config.skip_covered and self.skipped_count:
161 writeout(fmt_skip_covered % (skipped_count, 's' if skipped_count > 1 else '')) 131 self.writeout(
132 fmt_skip_covered % (self.skipped_count, 's' if self.skipped_count > 1 else '')
133 )
134 if self.config.skip_empty and self.empty_count:
135 self.writeout(
136 fmt_skip_empty % (self.empty_count, 's' if self.empty_count > 1 else '')
137 )
162 138
163 return total.n_statements and total.pc_covered 139 return self.total.n_statements and self.total.pc_covered
140
141 def report_one_file(self, fr, analysis):
142 """Report on just one file, the callback from report()."""
143 nums = analysis.numbers
144 self.total += nums
145
146 no_missing_lines = (nums.n_missing == 0)
147 no_missing_branches = (nums.n_partial_branches == 0)
148 if self.config.skip_covered and no_missing_lines and no_missing_branches:
149 # Don't report on 100% files.
150 self.skipped_count += 1
151 elif self.config.skip_empty and nums.n_statements == 0:
152 # Don't report on empty files.
153 self.empty_count += 1
154 else:
155 self.fr_analysis.append((fr, analysis))

eric ide

mercurial