coverage 7.6.10__cp312-cp312-musllinux_1_2_aarch64.whl → 7.12.0__cp312-cp312-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. coverage/__init__.py +3 -1
  2. coverage/__main__.py +3 -1
  3. coverage/annotate.py +2 -3
  4. coverage/bytecode.py +178 -4
  5. coverage/cmdline.py +330 -155
  6. coverage/collector.py +32 -43
  7. coverage/config.py +167 -63
  8. coverage/context.py +5 -6
  9. coverage/control.py +165 -86
  10. coverage/core.py +71 -34
  11. coverage/data.py +4 -5
  12. coverage/debug.py +113 -57
  13. coverage/disposition.py +2 -1
  14. coverage/env.py +29 -78
  15. coverage/exceptions.py +29 -7
  16. coverage/execfile.py +19 -14
  17. coverage/files.py +24 -19
  18. coverage/html.py +118 -75
  19. coverage/htmlfiles/coverage_html.js +12 -10
  20. coverage/htmlfiles/index.html +45 -10
  21. coverage/htmlfiles/pyfile.html +2 -2
  22. coverage/htmlfiles/style.css +54 -6
  23. coverage/htmlfiles/style.scss +85 -3
  24. coverage/inorout.py +62 -45
  25. coverage/jsonreport.py +22 -9
  26. coverage/lcovreport.py +16 -18
  27. coverage/misc.py +51 -47
  28. coverage/multiproc.py +12 -7
  29. coverage/numbits.py +4 -5
  30. coverage/parser.py +150 -251
  31. coverage/patch.py +166 -0
  32. coverage/phystokens.py +25 -26
  33. coverage/plugin.py +14 -14
  34. coverage/plugin_support.py +37 -36
  35. coverage/python.py +13 -14
  36. coverage/pytracer.py +31 -33
  37. coverage/regions.py +3 -2
  38. coverage/report.py +60 -44
  39. coverage/report_core.py +7 -10
  40. coverage/results.py +152 -68
  41. coverage/sqldata.py +261 -211
  42. coverage/sqlitedb.py +37 -29
  43. coverage/sysmon.py +237 -162
  44. coverage/templite.py +19 -7
  45. coverage/tomlconfig.py +13 -13
  46. coverage/tracer.cpython-312-aarch64-linux-musl.so +0 -0
  47. coverage/tracer.pyi +3 -1
  48. coverage/types.py +26 -23
  49. coverage/version.py +4 -19
  50. coverage/xmlreport.py +17 -14
  51. {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/METADATA +50 -28
  52. coverage-7.12.0.dist-info/RECORD +59 -0
  53. {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/WHEEL +1 -1
  54. coverage-7.6.10.dist-info/RECORD +0 -58
  55. {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/entry_points.txt +0 -0
  56. {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info/licenses}/LICENSE.txt +0 -0
  57. {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/top_level.txt +0 -0
coverage/results.py CHANGED
@@ -1,5 +1,5 @@
1
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
2
+ # For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
3
3
 
4
4
  """Results of coverage measurement."""
5
5
 
@@ -7,8 +7,7 @@ from __future__ import annotations
7
7
 
8
8
  import collections
9
9
  import dataclasses
10
-
11
- from collections.abc import Container, Iterable
10
+ from collections.abc import Iterable
12
11
  from typing import TYPE_CHECKING
13
12
 
14
13
  from coverage.exceptions import ConfigError
@@ -42,9 +41,7 @@ def analysis_from_file_reporter(
42
41
  for fromno, tono in arc_possibilities_set:
43
42
  dests[fromno].add(tono)
44
43
  single_dests = {
45
- fromno: list(tonos)[0]
46
- for fromno, tonos in dests.items()
47
- if len(tonos) == 1
44
+ fromno: list(tonos)[0] for fromno, tonos in dests.items() if len(tonos) == 1
48
45
  }
49
46
  new_arcs = set()
50
47
  for fromno, tono in arcs:
@@ -100,8 +97,8 @@ class Analysis:
100
97
  if self.has_arcs:
101
98
  n_branches = self._total_branches()
102
99
  mba = self.missing_branch_arcs()
103
- n_partial_branches = sum(len(v) for k,v in mba.items() if k not in self.missing)
104
- n_missing_branches = sum(len(v) for k,v in mba.items())
100
+ n_partial_branches = sum(len(v) for k, v in mba.items() if k not in self.missing)
101
+ n_missing_branches = sum(len(v) for k, v in mba.items())
105
102
  else:
106
103
  n_branches = n_partial_branches = n_missing_branches = 0
107
104
 
@@ -116,50 +113,6 @@ class Analysis:
116
113
  n_missing_branches=n_missing_branches,
117
114
  )
118
115
 
119
- def narrow(self, lines: Container[TLineNo]) -> Analysis:
120
- """Create a narrowed Analysis.
121
-
122
- The current analysis is copied to make a new one that only considers
123
- the lines in `lines`.
124
- """
125
-
126
- statements = {lno for lno in self.statements if lno in lines}
127
- excluded = {lno for lno in self.excluded if lno in lines}
128
- executed = {lno for lno in self.executed if lno in lines}
129
-
130
- if self.has_arcs:
131
- arc_possibilities_set = {
132
- (a, b) for a, b in self.arc_possibilities_set
133
- if a in lines or b in lines
134
- }
135
- arcs_executed_set = {
136
- (a, b) for a, b in self.arcs_executed_set
137
- if a in lines or b in lines
138
- }
139
- exit_counts = {
140
- lno: num for lno, num in self.exit_counts.items()
141
- if lno in lines
142
- }
143
- no_branch = {lno for lno in self.no_branch if lno in lines}
144
- else:
145
- arc_possibilities_set = set()
146
- arcs_executed_set = set()
147
- exit_counts = {}
148
- no_branch = set()
149
-
150
- return Analysis(
151
- precision=self.precision,
152
- filename=self.filename,
153
- has_arcs=self.has_arcs,
154
- statements=statements,
155
- excluded=excluded,
156
- executed=executed,
157
- arc_possibilities_set=arc_possibilities_set,
158
- arcs_executed_set=arcs_executed_set,
159
- exit_counts=exit_counts,
160
- no_branch=no_branch,
161
- )
162
-
163
116
  def missing_formatted(self, branches: bool = False) -> str:
164
117
  """The missing line numbers, formatted nicely.
165
118
 
@@ -178,16 +131,17 @@ class Analysis:
178
131
  def arcs_missing(self) -> list[TArc]:
179
132
  """Returns a sorted list of the un-executed arcs in the code."""
180
133
  missing = (
181
- p for p in self.arc_possibilities
182
- if p not in self.arcs_executed_set
183
- and p[0] not in self.no_branch
184
- and p[1] not in self.excluded
134
+ p
135
+ for p in self.arc_possibilities
136
+ if p not in self.arcs_executed_set
137
+ and p[0] not in self.no_branch
138
+ and p[1] not in self.excluded
185
139
  )
186
140
  return sorted(missing)
187
141
 
188
142
  def _branch_lines(self) -> list[TLineNo]:
189
143
  """Returns a list of line numbers that have more than one exit."""
190
- return [l1 for l1,count in self.exit_counts.items() if count > 1]
144
+ return [l1 for l1, count in self.exit_counts.items() if count > 1]
191
145
 
192
146
  def _total_branches(self) -> int:
193
147
  """How many total branches are there?"""
@@ -231,6 +185,7 @@ class Analysis:
231
185
 
232
186
  Returns a dict mapping line numbers to a tuple:
233
187
  (total_exits, taken_exits).
188
+
234
189
  """
235
190
 
236
191
  missing_arcs = self.missing_branch_arcs()
@@ -242,6 +197,104 @@ class Analysis:
242
197
  return stats
243
198
 
244
199
 
200
+ TRegionLines = frozenset[TLineNo]
201
+
202
+
203
+ class AnalysisNarrower:
204
+ """
205
+ For reducing an `Analysis` to a subset of its lines.
206
+
207
+ Originally this was a simpler method on Analysis, but that led to quadratic
208
+ behavior. This class does the bulk of the work up-front to provide the
209
+ same results in linear time.
210
+
211
+ Create an AnalysisNarrower from an Analysis, bulk-add region lines to it
212
+ with `add_regions`, then individually request new narrowed Analysis objects
213
+ for each region with `narrow`. Doing most of the work in limited calls to
214
+ `add_regions` lets us avoid poor performance.
215
+ """
216
+
217
+ # In this class, regions are represented by a frozenset of their lines.
218
+
219
+ def __init__(self, analysis: Analysis) -> None:
220
+ self.analysis = analysis
221
+ self.region2arc_possibilities: dict[TRegionLines, set[TArc]] = collections.defaultdict(set)
222
+ self.region2arc_executed: dict[TRegionLines, set[TArc]] = collections.defaultdict(set)
223
+ self.region2exit_counts: dict[TRegionLines, dict[TLineNo, int]] = collections.defaultdict(
224
+ dict
225
+ )
226
+
227
+ def add_regions(self, liness: Iterable[set[TLineNo]]) -> None:
228
+ """
229
+ Pre-process a number of sets of line numbers. Later calls to `narrow`
230
+ with one of these sets will provide a narrowed Analysis.
231
+ """
232
+ if self.analysis.has_arcs:
233
+ line2region: dict[TLineNo, TRegionLines] = {}
234
+
235
+ for lines in liness:
236
+ fzlines = frozenset(lines)
237
+ for line in lines:
238
+ line2region[line] = fzlines
239
+
240
+ def collect_arcs(
241
+ arc_set: set[TArc],
242
+ region2arcs: dict[TRegionLines, set[TArc]],
243
+ ) -> None:
244
+ for a, b in arc_set:
245
+ if r := line2region.get(a):
246
+ region2arcs[r].add((a, b))
247
+ if r := line2region.get(b):
248
+ region2arcs[r].add((a, b))
249
+
250
+ collect_arcs(self.analysis.arc_possibilities_set, self.region2arc_possibilities)
251
+ collect_arcs(self.analysis.arcs_executed_set, self.region2arc_executed)
252
+
253
+ for lno, num in self.analysis.exit_counts.items():
254
+ if r := line2region.get(lno):
255
+ self.region2exit_counts[r][lno] = num
256
+
257
+ def narrow(self, lines: set[TLineNo]) -> Analysis:
258
+ """Create a narrowed Analysis.
259
+
260
+ The current analysis is copied to make a new one that only considers
261
+ the lines in `lines`.
262
+ """
263
+
264
+ # Technically, the set intersections in this method are still O(N**2)
265
+ # since this method is called N times, but they're very fast and moving
266
+ # them to `add_regions` won't avoid the quadratic time.
267
+
268
+ statements = self.analysis.statements & lines
269
+ excluded = self.analysis.excluded & lines
270
+ executed = self.analysis.executed & lines
271
+
272
+ if self.analysis.has_arcs:
273
+ fzlines = frozenset(lines)
274
+ arc_possibilities_set = self.region2arc_possibilities[fzlines]
275
+ arcs_executed_set = self.region2arc_executed[fzlines]
276
+ exit_counts = self.region2exit_counts[fzlines]
277
+ no_branch = self.analysis.no_branch & lines
278
+ else:
279
+ arc_possibilities_set = set()
280
+ arcs_executed_set = set()
281
+ exit_counts = {}
282
+ no_branch = set()
283
+
284
+ return Analysis(
285
+ precision=self.analysis.precision,
286
+ filename=self.analysis.filename,
287
+ has_arcs=self.analysis.has_arcs,
288
+ statements=statements,
289
+ excluded=excluded,
290
+ executed=executed,
291
+ arc_possibilities_set=arc_possibilities_set,
292
+ arcs_executed_set=arcs_executed_set,
293
+ exit_counts=exit_counts,
294
+ no_branch=no_branch,
295
+ )
296
+
297
+
245
298
  @dataclasses.dataclass
246
299
  class Numbers:
247
300
  """The numerical results of measuring coverage.
@@ -270,15 +323,36 @@ class Numbers:
270
323
  """Returns the number of executed branches."""
271
324
  return self.n_branches - self.n_missing_branches
272
325
 
326
+ @property
327
+ def ratio_statements(self) -> tuple[int, int]:
328
+ """Return numerator/denominator for statement coverage."""
329
+ return self.n_executed, self.n_statements
330
+
331
+ @property
332
+ def ratio_branches(self) -> tuple[int, int]:
333
+ """Return numerator/denominator for branch coverage."""
334
+ return self.n_executed_branches, self.n_branches
335
+
336
+ def _percent(self, numerator: int, denominator: int) -> float:
337
+ """Helper for pc_* properties."""
338
+ if denominator > 0:
339
+ return (100.0 * numerator) / denominator
340
+ return 100.0
341
+
273
342
  @property
274
343
  def pc_covered(self) -> float:
275
344
  """Returns a single percentage value for coverage."""
276
- if self.n_statements > 0:
277
- numerator, denominator = self.ratio_covered
278
- pc_cov = (100.0 * numerator) / denominator
279
- else:
280
- pc_cov = 100.0
281
- return pc_cov
345
+ return self._percent(*self.ratio_covered)
346
+
347
+ @property
348
+ def pc_statements(self) -> float:
349
+ """Returns the percentage covered for statements."""
350
+ return self._percent(*self.ratio_statements)
351
+
352
+ @property
353
+ def pc_branches(self) -> float:
354
+ """Returns the percentage covered for branches."""
355
+ return self._percent(*self.ratio_branches)
282
356
 
283
357
  @property
284
358
  def pc_covered_str(self) -> str:
@@ -291,6 +365,16 @@ class Numbers:
291
365
  """
292
366
  return display_covered(self.pc_covered, self.precision)
293
367
 
368
+ @property
369
+ def pc_statements_str(self) -> str:
370
+ """Returns the statement percent covered without a percent sign."""
371
+ return display_covered(self.pc_statements, self.precision)
372
+
373
+ @property
374
+ def pc_branches_str(self) -> str:
375
+ """Returns the branch percent covered without a percent sign."""
376
+ return display_covered(self.pc_branches, self.precision)
377
+
294
378
  @property
295
379
  def ratio_covered(self) -> tuple[int, int]:
296
380
  """Return a numerator and denominator for the coverage ratio."""
@@ -312,7 +396,7 @@ class Numbers:
312
396
 
313
397
  def __radd__(self, other: int) -> Numbers:
314
398
  # Implementing 0+Numbers allows us to sum() a list of Numbers.
315
- assert other == 0 # we only ever call it this way.
399
+ assert other == 0 # we only ever call it this way.
316
400
  return self
317
401
 
318
402
 
@@ -324,14 +408,14 @@ def display_covered(pc: float, precision: int) -> str:
324
408
  result in either "0" or "100".
325
409
 
326
410
  """
327
- near0 = 1.0 / 10 ** precision
411
+ near0 = 1.0 / 10**precision
328
412
  if 0 < pc < near0:
329
413
  pc = near0
330
414
  elif (100.0 - near0) < pc < 100:
331
415
  pc = 100.0 - near0
332
416
  else:
333
417
  pc = round(pc, precision)
334
- return "%.*f" % (precision, pc)
418
+ return f"{pc:.{precision}f}"
335
419
 
336
420
 
337
421
  def _line_ranges(
@@ -343,7 +427,7 @@ def _line_ranges(
343
427
  lines = sorted(lines)
344
428
 
345
429
  pairs = []
346
- start = None
430
+ start: TLineNo | None = None
347
431
  lidx = 0
348
432
  for stmt in statements:
349
433
  if lidx >= len(lines):
@@ -389,7 +473,7 @@ def format_lines(
389
473
  for line, exits in line_exits:
390
474
  for ex in sorted(exits):
391
475
  if line not in lines and ex not in lines:
392
- dest = (ex if ex > 0 else "exit")
476
+ dest = ex if ex > 0 else "exit"
393
477
  line_items.append((line, f"{line}->{dest}"))
394
478
 
395
479
  ret = ", ".join(t[-1] for t in sorted(line_items))