eric7/DebugClients/Python/coverage/results.py

branch
eric7
changeset 8775
0802ae193343
parent 8527
2bd1325d727e
child 8929
fcca2fa618bf
equal deleted inserted replaced
8774:d728227e8ebb 8775:0802ae193343
3 3
4 """Results of coverage measurement.""" 4 """Results of coverage measurement."""
5 5
6 import collections 6 import collections
7 7
8 from coverage.backward import iitems
9 from coverage.debug import SimpleReprMixin 8 from coverage.debug import SimpleReprMixin
10 from coverage.misc import contract, CoverageException, nice_pair 9 from coverage.exceptions import CoverageException
11 10 from coverage.misc import contract, nice_pair
12 11
13 class Analysis(object): 12
13 class Analysis:
14 """The results of analyzing a FileReporter.""" 14 """The results of analyzing a FileReporter."""
15 15
16 def __init__(self, data, file_reporter, file_mapper): 16 def __init__(self, data, precision, file_reporter, file_mapper):
17 self.data = data 17 self.data = data
18 self.file_reporter = file_reporter 18 self.file_reporter = file_reporter
19 self.filename = file_mapper(self.file_reporter.filename) 19 self.filename = file_mapper(self.file_reporter.filename)
20 self.statements = self.file_reporter.lines() 20 self.statements = self.file_reporter.lines()
21 self.excluded = self.file_reporter.excluded_lines() 21 self.excluded = self.file_reporter.excluded_lines()
30 self._arc_possibilities = sorted(self.file_reporter.arcs()) 30 self._arc_possibilities = sorted(self.file_reporter.arcs())
31 self.exit_counts = self.file_reporter.exit_counts() 31 self.exit_counts = self.file_reporter.exit_counts()
32 self.no_branch = self.file_reporter.no_branch_lines() 32 self.no_branch = self.file_reporter.no_branch_lines()
33 n_branches = self._total_branches() 33 n_branches = self._total_branches()
34 mba = self.missing_branch_arcs() 34 mba = self.missing_branch_arcs()
35 n_partial_branches = sum(len(v) for k,v in iitems(mba) if k not in self.missing) 35 n_partial_branches = sum(len(v) for k,v in mba.items() if k not in self.missing)
36 n_missing_branches = sum(len(v) for k,v in iitems(mba)) 36 n_missing_branches = sum(len(v) for k,v in mba.items())
37 else: 37 else:
38 self._arc_possibilities = [] 38 self._arc_possibilities = []
39 self.exit_counts = {} 39 self.exit_counts = {}
40 self.no_branch = set() 40 self.no_branch = set()
41 n_branches = n_partial_branches = n_missing_branches = 0 41 n_branches = n_partial_branches = n_missing_branches = 0
42 42
43 self.numbers = Numbers( 43 self.numbers = Numbers(
44 precision=precision,
44 n_files=1, 45 n_files=1,
45 n_statements=len(self.statements), 46 n_statements=len(self.statements),
46 n_excluded=len(self.excluded), 47 n_excluded=len(self.excluded),
47 n_missing=len(self.missing), 48 n_missing=len(self.missing),
48 n_branches=n_branches, 49 n_branches=n_branches,
57 58
58 If `branches` is true, includes the missing branch arcs also. 59 If `branches` is true, includes the missing branch arcs also.
59 60
60 """ 61 """
61 if branches and self.has_arcs(): 62 if branches and self.has_arcs():
62 arcs = iitems(self.missing_branch_arcs()) 63 arcs = self.missing_branch_arcs().items()
63 else: 64 else:
64 arcs = None 65 arcs = None
65 66
66 return format_lines(self.statements, self.missing, arcs=arcs) 67 return format_lines(self.statements, self.missing, arcs=arcs)
67 68
81 executed = self.file_reporter.translate_arcs(executed) 82 executed = self.file_reporter.translate_arcs(executed)
82 return sorted(executed) 83 return sorted(executed)
83 84
84 @contract(returns='list(tuple(int, int))') 85 @contract(returns='list(tuple(int, int))')
85 def arcs_missing(self): 86 def arcs_missing(self):
86 """Returns a sorted list of the arcs in the code not executed.""" 87 """Returns a sorted list of the unexecuted arcs in the code."""
87 possible = self.arc_possibilities() 88 possible = self.arc_possibilities()
88 executed = self.arcs_executed() 89 executed = self.arcs_executed()
89 missing = ( 90 missing = (
90 p for p in possible 91 p for p in possible
91 if p not in executed 92 if p not in executed
92 and p[0] not in self.no_branch 93 and p[0] not in self.no_branch
94 and p[1] not in self.excluded
93 ) 95 )
94 return sorted(missing) 96 return sorted(missing)
95 97
96 @contract(returns='list(tuple(int, int))') 98 @contract(returns='list(tuple(int, int))')
97 def arcs_unpredicted(self): 99 def arcs_unpredicted(self):
111 ) 113 )
112 return sorted(unpredicted) 114 return sorted(unpredicted)
113 115
114 def _branch_lines(self): 116 def _branch_lines(self):
115 """Returns a list of line numbers that have more than one exit.""" 117 """Returns a list of line numbers that have more than one exit."""
116 return [l1 for l1,count in iitems(self.exit_counts) if count > 1] 118 return [l1 for l1,count in self.exit_counts.items() if count > 1]
117 119
118 def _total_branches(self): 120 def _total_branches(self):
119 """How many total branches are there?""" 121 """How many total branches are there?"""
120 return sum(count for count in self.exit_counts.values() if count > 1) 122 return sum(count for count in self.exit_counts.values() if count > 1)
121 123
156 158
157 This holds the basic statistics from `Analysis`, and is used to roll 159 This holds the basic statistics from `Analysis`, and is used to roll
158 up statistics across files. 160 up statistics across files.
159 161
160 """ 162 """
161 # A global to determine the precision on coverage percentages, the number 163
162 # of decimal places. 164 def __init__(self,
163 _precision = 0 165 precision=0,
164 _near0 = 1.0 # These will change when _precision is changed. 166 n_files=0, n_statements=0, n_excluded=0, n_missing=0,
165 _near100 = 99.0 167 n_branches=0, n_partial_branches=0, n_missing_branches=0
166 168 ):
167 def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0, 169 assert 0 <= precision < 10
168 n_branches=0, n_partial_branches=0, n_missing_branches=0 170 self._precision = precision
169 ): 171 self._near0 = 1.0 / 10**precision
172 self._near100 = 100.0 - self._near0
170 self.n_files = n_files 173 self.n_files = n_files
171 self.n_statements = n_statements 174 self.n_statements = n_statements
172 self.n_excluded = n_excluded 175 self.n_excluded = n_excluded
173 self.n_missing = n_missing 176 self.n_missing = n_missing
174 self.n_branches = n_branches 177 self.n_branches = n_branches
176 self.n_missing_branches = n_missing_branches 179 self.n_missing_branches = n_missing_branches
177 180
178 def init_args(self): 181 def init_args(self):
179 """Return a list for __init__(*args) to recreate this object.""" 182 """Return a list for __init__(*args) to recreate this object."""
180 return [ 183 return [
184 self._precision,
181 self.n_files, self.n_statements, self.n_excluded, self.n_missing, 185 self.n_files, self.n_statements, self.n_excluded, self.n_missing,
182 self.n_branches, self.n_partial_branches, self.n_missing_branches, 186 self.n_branches, self.n_partial_branches, self.n_missing_branches,
183 ] 187 ]
184
185 @classmethod
186 def set_precision(cls, precision):
187 """Set the number of decimal places used to report percentages."""
188 assert 0 <= precision < 10
189 cls._precision = precision
190 cls._near0 = 1.0 / 10**precision
191 cls._near100 = 100.0 - cls._near0
192 188
193 @property 189 @property
194 def n_executed(self): 190 def n_executed(self):
195 """Returns the number of executed statements.""" 191 """Returns the number of executed statements."""
196 return self.n_statements - self.n_missing 192 return self.n_statements - self.n_missing
217 Note that "0" is only returned when the value is truly zero, and "100" 213 Note that "0" is only returned when the value is truly zero, and "100"
218 is only returned when the value is truly 100. Rounding can never 214 is only returned when the value is truly 100. Rounding can never
219 result in either "0" or "100". 215 result in either "0" or "100".
220 216
221 """ 217 """
222 pc = self.pc_covered 218 return self.display_covered(self.pc_covered)
219
220 def display_covered(self, pc):
221 """Return a displayable total percentage, as a string.
222
223 Note that "0" is only returned when the value is truly zero, and "100"
224 is only returned when the value is truly 100. Rounding can never
225 result in either "0" or "100".
226
227 """
223 if 0 < pc < self._near0: 228 if 0 < pc < self._near0:
224 pc = self._near0 229 pc = self._near0
225 elif self._near100 < pc < 100: 230 elif self._near100 < pc < 100:
226 pc = self._near100 231 pc = self._near100
227 else: 232 else:
228 pc = round(pc, self._precision) 233 pc = round(pc, self._precision)
229 return "%.*f" % (self._precision, pc) 234 return "%.*f" % (self._precision, pc)
230 235
231 @classmethod 236 def pc_str_width(self):
232 def pc_str_width(cls):
233 """How many characters wide can pc_covered_str be?""" 237 """How many characters wide can pc_covered_str be?"""
234 width = 3 # "100" 238 width = 3 # "100"
235 if cls._precision > 0: 239 if self._precision > 0:
236 width += 1 + cls._precision 240 width += 1 + self._precision
237 return width 241 return width
238 242
239 @property 243 @property
240 def ratio_covered(self): 244 def ratio_covered(self):
241 """Return a numerator and denominator for the coverage ratio.""" 245 """Return a numerator and denominator for the coverage ratio."""
242 numerator = self.n_executed + self.n_executed_branches 246 numerator = self.n_executed + self.n_executed_branches
243 denominator = self.n_statements + self.n_branches 247 denominator = self.n_statements + self.n_branches
244 return numerator, denominator 248 return numerator, denominator
245 249
246 def __add__(self, other): 250 def __add__(self, other):
247 nums = Numbers() 251 nums = Numbers(precision=self._precision)
248 nums.n_files = self.n_files + other.n_files 252 nums.n_files = self.n_files + other.n_files
249 nums.n_statements = self.n_statements + other.n_statements 253 nums.n_statements = self.n_statements + other.n_statements
250 nums.n_excluded = self.n_excluded + other.n_excluded 254 nums.n_excluded = self.n_excluded + other.n_excluded
251 nums.n_missing = self.n_missing + other.n_missing 255 nums.n_missing = self.n_missing + other.n_missing
252 nums.n_branches = self.n_branches + other.n_branches 256 nums.n_branches = self.n_branches + other.n_branches
258 ) 262 )
259 return nums 263 return nums
260 264
261 def __radd__(self, other): 265 def __radd__(self, other):
262 # Implementing 0+Numbers allows us to sum() a list of Numbers. 266 # Implementing 0+Numbers allows us to sum() a list of Numbers.
263 if other == 0: 267 assert other == 0 # we only ever call it this way.
264 return self 268 return self
265 return NotImplemented # pragma: not covered (we never call it this way)
266 269
267 270
268 def _line_ranges(statements, lines): 271 def _line_ranges(statements, lines):
269 """Produce a list of ranges for `format_lines`.""" 272 """Produce a list of ranges for `format_lines`."""
270 statements = sorted(statements) 273 statements = sorted(statements)
331 Returns True if the total should fail. 334 Returns True if the total should fail.
332 335
333 """ 336 """
334 # We can never achieve higher than 100% coverage, or less than zero. 337 # We can never achieve higher than 100% coverage, or less than zero.
335 if not (0 <= fail_under <= 100.0): 338 if not (0 <= fail_under <= 100.0):
336 msg = "fail_under={} is invalid. Must be between 0 and 100.".format(fail_under) 339 msg = f"fail_under={fail_under} is invalid. Must be between 0 and 100."
337 raise CoverageException(msg) 340 raise CoverageException(msg)
338 341
339 # Special case for fail_under=100, it must really be 100. 342 # Special case for fail_under=100, it must really be 100.
340 if fail_under == 100.0 and total != 100.0: 343 if fail_under == 100.0 and total != 100.0:
341 return True 344 return True

eric ide

mercurial