eric7/DebugClients/Python/coverage/summary.py

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

eric ide

mercurial