eric6/DebugClients/Python/coverage/summary.py

changeset 6942
2602857055c5
parent 6219
d6c795b5ce33
child 7427
362cd1b6f81a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
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
3
4 """Summary reporting"""
5
6 import sys
7
8 from coverage import env
9 from coverage.report import Reporter
10 from coverage.results import Numbers
11 from coverage.misc import NotPython, CoverageException, output_encoding, StopEverything
12
13
14 class SummaryReporter(Reporter):
15 """A reporter for writing the summary report."""
16
17 def __init__(self, coverage, config):
18 super(SummaryReporter, self).__init__(coverage, config)
19 self.branches = coverage.data.has_arcs()
20
21 def report(self, morfs, outfile=None):
22 """Writes a report summarizing coverage statistics per module.
23
24 `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).
26
27 """
28 if outfile is None:
29 outfile = sys.stdout
30
31 def writeout(line):
32 """Write a line to the output, adding a newline."""
33 if env.PY2:
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
72 # Prepare the formatting strings, header, and column sorting.
73 max_name = max([len(fr.relative_filename()) for (fr, analysis) in fr_analysis] + [5])
74 fmt_name = u"%%- %ds " % max_name
75 fmt_skip_covered = u"\n%s file%s skipped due to complete coverage."
76
77 header = (fmt_name % "Name") + u" Stmts Miss"
78 fmt_coverage = fmt_name + u"%6d %6d"
79 if self.branches:
80 header += u" Branch BrPart"
81 fmt_coverage += u" %6d %6d"
82 width100 = Numbers.pc_str_width()
83 header += u"%*s" % (width100+4, "Cover")
84 fmt_coverage += u"%%%ds%%%%" % (width100+3,)
85 if self.config.show_missing:
86 header += u" Missing"
87 fmt_coverage += u" %s"
88 rule = u"-" * len(header)
89
90 column_order = dict(name=0, stmts=1, miss=2, cover=-1)
91 if self.branches:
92 column_order.update(dict(branch=3, brpart=4))
93
94 # Write the header
95 writeout(header)
96 writeout(rule)
97
98 # `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
100 # sortable values.
101 lines = []
102
103 for (fr, analysis) in fr_analysis:
104 try:
105 nums = analysis.numbers
106
107 args = (fr.relative_filename(), nums.n_statements, nums.n_missing)
108 if self.branches:
109 args += (nums.n_branches, nums.n_partial_branches)
110 args += (nums.pc_covered_str,)
111 if self.config.show_missing:
112 missing_fmtd = analysis.missing_formatted()
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
121 # Add numeric percent coverage so that sorting makes sense.
122 args += (nums.pc_covered,)
123 lines.append((text, args))
124 except Exception:
125 report_it = not self.config.ignore_errors
126 if report_it:
127 typ, msg = sys.exc_info()[:2]
128 # NotPython is only raised by PythonFileReporter, which has a
129 # should_be_python() method.
130 if typ is NotPython and not fr.should_be_python():
131 report_it = False
132 if report_it:
133 writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg))
134
135 # Sort the lines and write them out.
136 if getattr(self.config, 'sort', None):
137 position = column_order.get(self.config.sort.lower())
138 if position is None:
139 raise CoverageException("Invalid sorting option: {0!r}".format(self.config.sort))
140 lines.sort(key=lambda l: (l[1][position], l[0]))
141
142 for line in lines:
143 writeout(line[0])
144
145 # Write a TOTAl line if we had more than one file.
146 if total.n_files > 1:
147 writeout(rule)
148 args = ("TOTAL", total.n_statements, total.n_missing)
149 if self.branches:
150 args += (total.n_branches, total.n_partial_branches)
151 args += (total.pc_covered_str,)
152 if self.config.show_missing:
153 args += ("",)
154 writeout(fmt_coverage % args)
155
156 # Write other final lines.
157 if not total.n_files and not skipped_count:
158 raise CoverageException("No data to report.")
159
160 if self.config.skip_covered and skipped_count:
161 writeout(fmt_skip_covered % (skipped_count, 's' if skipped_count > 1 else ''))
162
163 return total.n_statements and total.pc_covered

eric ide

mercurial