coverage 7.6.7__cp311-cp311-win_amd64.whl → 7.11.1__cp311-cp311-win_amd64.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 (54) hide show
  1. coverage/__init__.py +2 -0
  2. coverage/__main__.py +2 -0
  3. coverage/annotate.py +1 -2
  4. coverage/bytecode.py +177 -3
  5. coverage/cmdline.py +329 -154
  6. coverage/collector.py +31 -42
  7. coverage/config.py +166 -62
  8. coverage/context.py +4 -5
  9. coverage/control.py +164 -85
  10. coverage/core.py +70 -33
  11. coverage/data.py +3 -4
  12. coverage/debug.py +112 -56
  13. coverage/disposition.py +1 -0
  14. coverage/env.py +65 -55
  15. coverage/exceptions.py +35 -7
  16. coverage/execfile.py +18 -13
  17. coverage/files.py +23 -18
  18. coverage/html.py +134 -88
  19. coverage/htmlfiles/style.css +42 -2
  20. coverage/htmlfiles/style.scss +65 -1
  21. coverage/inorout.py +61 -44
  22. coverage/jsonreport.py +17 -8
  23. coverage/lcovreport.py +16 -20
  24. coverage/misc.py +50 -46
  25. coverage/multiproc.py +12 -7
  26. coverage/numbits.py +3 -4
  27. coverage/parser.py +193 -269
  28. coverage/patch.py +166 -0
  29. coverage/phystokens.py +24 -25
  30. coverage/plugin.py +13 -13
  31. coverage/plugin_support.py +36 -35
  32. coverage/python.py +9 -13
  33. coverage/pytracer.py +40 -33
  34. coverage/regions.py +2 -1
  35. coverage/report.py +59 -43
  36. coverage/report_core.py +6 -9
  37. coverage/results.py +118 -66
  38. coverage/sqldata.py +260 -210
  39. coverage/sqlitedb.py +33 -25
  40. coverage/sysmon.py +195 -157
  41. coverage/templite.py +6 -6
  42. coverage/tomlconfig.py +12 -12
  43. coverage/tracer.cp311-win_amd64.pyd +0 -0
  44. coverage/tracer.pyi +2 -0
  45. coverage/types.py +25 -22
  46. coverage/version.py +3 -18
  47. coverage/xmlreport.py +16 -13
  48. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info}/METADATA +40 -18
  49. coverage-7.11.1.dist-info/RECORD +59 -0
  50. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info}/WHEEL +1 -1
  51. coverage-7.6.7.dist-info/RECORD +0 -58
  52. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info}/entry_points.txt +0 -0
  53. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info/licenses}/LICENSE.txt +0 -0
  54. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info}/top_level.txt +0 -0
coverage/pytracer.py CHANGED
@@ -10,7 +10,6 @@ import dis
10
10
  import itertools
11
11
  import sys
12
12
  import threading
13
-
14
13
  from types import FrameType, ModuleType
15
14
  from typing import Any, Callable, cast
16
15
 
@@ -19,15 +18,23 @@ from coverage.types import (
19
18
  TArc,
20
19
  TFileDisposition,
21
20
  TLineNo,
21
+ Tracer,
22
22
  TShouldStartContextFn,
23
23
  TShouldTraceFn,
24
24
  TTraceData,
25
25
  TTraceFileData,
26
26
  TTraceFn,
27
27
  TWarnFn,
28
- Tracer,
29
28
  )
30
29
 
30
+ # I don't understand why, but if we use `cast(set[TLineNo], ...)` inside
31
+ # the _trace() function, we get some strange behavior on PyPy 3.10.
32
+ # Assigning these names here and using them below fixes the problem.
33
+ # See https://github.com/nedbat/coveragepy/issues/1902
34
+ set_TLineNo = set[TLineNo]
35
+ set_TArc = set[TArc]
36
+
37
+
31
38
  # We need the YIELD_VALUE opcode below, in a comparison-friendly form.
32
39
  # PYVERSIONS: RESUME is new in Python3.11
33
40
  RESUME = dis.opmap.get("RESUME")
@@ -44,6 +51,7 @@ else:
44
51
 
45
52
  THIS_FILE = __file__.rstrip("co")
46
53
 
54
+
47
55
  class PyTracer(Tracer):
48
56
  """Python implementation of the raw data tracer."""
49
57
 
@@ -117,19 +125,20 @@ class PyTracer(Tracer):
117
125
 
118
126
  def log(self, marker: str, *args: Any) -> None:
119
127
  """For hard-core logging of what this tracer is doing."""
120
- with open("/tmp/debug_trace.txt", "a") as f:
128
+ with open("/tmp/debug_trace.txt", "a", encoding="utf-8") as f:
121
129
  f.write(f"{marker} {self.id}[{len(self.data_stack)}]")
122
- if 0: # if you want thread ids..
123
- f.write(".{:x}.{:x}".format( # type: ignore[unreachable]
124
- self.thread.ident,
125
- self.threading.current_thread().ident,
126
- ))
130
+ if 0: # if you want thread ids..
131
+ f.write( # type: ignore[unreachable]
132
+ ".{:x}.{:x}".format(
133
+ self.thread.ident,
134
+ self.threading.current_thread().ident,
135
+ )
136
+ )
127
137
  f.write(" {}".format(" ".join(map(str, args))))
128
- if 0: # if you want callers..
129
- f.write(" | ") # type: ignore[unreachable]
138
+ if 0: # if you want callers..
139
+ f.write(" | ") # type: ignore[unreachable]
130
140
  stack = " / ".join(
131
- (fname or "???").rpartition("/")[-1]
132
- for _, fname, _, _ in self.data_stack
141
+ (fname or "???").rpartition("/")[-1] for _, fname, _, _ in self.data_stack
133
142
  )
134
143
  f.write(stack)
135
144
  f.write("\n")
@@ -138,8 +147,8 @@ class PyTracer(Tracer):
138
147
  self,
139
148
  frame: FrameType,
140
149
  event: str,
141
- arg: Any, # pylint: disable=unused-argument
142
- lineno: TLineNo | None = None, # pylint: disable=unused-argument
150
+ arg: Any, # pylint: disable=unused-argument
151
+ lineno: TLineNo | None = None, # pylint: disable=unused-argument
143
152
  ) -> TTraceFn | None:
144
153
  """The trace function passed to sys.settrace."""
145
154
 
@@ -149,11 +158,11 @@ class PyTracer(Tracer):
149
158
  # f = frame; code = f.f_code
150
159
  # self.log(":", f"{code.co_filename} {f.f_lineno} {code.co_name}()", event)
151
160
 
152
- if (self.stopped and sys.gettrace() == self._cached_bound_method_trace): # pylint: disable=comparison-with-callable
161
+ if self.stopped and sys.gettrace() == self._cached_bound_method_trace: # pylint: disable=comparison-with-callable
153
162
  # The PyTrace.stop() method has been called, possibly by another
154
163
  # thread, let's deactivate ourselves now.
155
164
  if 0:
156
- f = frame # type: ignore[unreachable]
165
+ f = frame # type: ignore[unreachable]
157
166
  self.log("---\nX", f.f_code.co_filename, f.f_lineno)
158
167
  while f:
159
168
  self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace)
@@ -178,12 +187,12 @@ class PyTracer(Tracer):
178
187
  if event == "call":
179
188
  # Should we start a new context?
180
189
  if self.should_start_context and self.context is None:
181
- context_maybe = self.should_start_context(frame) # pylint: disable=not-callable
190
+ context_maybe = self.should_start_context(frame) # pylint: disable=not-callable
182
191
  if context_maybe is not None:
183
192
  self.context = context_maybe
184
193
  started_context = True
185
194
  assert self.switch_context is not None
186
- self.switch_context(self.context) # pylint: disable=not-callable
195
+ self.switch_context(self.context) # pylint: disable=not-callable
187
196
  else:
188
197
  started_context = False
189
198
  else:
@@ -239,9 +248,9 @@ class PyTracer(Tracer):
239
248
  # The current opcode is guaranteed to be RESUME. The argument
240
249
  # determines what kind of resume it is.
241
250
  oparg = frame.f_code.co_code[frame.f_lasti + 1]
242
- real_call = (oparg == 0)
251
+ real_call = (oparg == 0) # fmt: skip
243
252
  else:
244
- real_call = (getattr(frame, "f_lasti", -1) < 0)
253
+ real_call = (getattr(frame, "f_lasti", -1) < 0) # fmt: skip
245
254
  if real_call:
246
255
  self.last_line = -frame.f_code.co_firstlineno
247
256
  else:
@@ -253,9 +262,9 @@ class PyTracer(Tracer):
253
262
  flineno: TLineNo = frame.f_lineno
254
263
 
255
264
  if self.trace_arcs:
256
- cast(set[TArc], self.cur_file_data).add((self.last_line, flineno))
265
+ cast(set_TArc, self.cur_file_data).add((self.last_line, flineno))
257
266
  else:
258
- cast(set[TLineNo], self.cur_file_data).add(flineno)
267
+ cast(set_TLineNo, self.cur_file_data).add(flineno)
259
268
  self.last_line = flineno
260
269
 
261
270
  elif event == "return":
@@ -272,7 +281,7 @@ class PyTracer(Tracer):
272
281
  # It is a real return if we aren't going to resume next.
273
282
  if env.PYBEHAVIOR.lasti_is_yield:
274
283
  lasti += 2
275
- real_return = (code[lasti] != RESUME)
284
+ real_return = code[lasti] != RESUME
276
285
  else:
277
286
  if code[lasti] == RETURN_VALUE:
278
287
  real_return = True
@@ -286,7 +295,7 @@ class PyTracer(Tracer):
286
295
  real_return = True
287
296
  if real_return:
288
297
  first = frame.f_code.co_firstlineno
289
- cast(set[TArc], self.cur_file_data).add((self.last_line, -first))
298
+ cast(set_TArc, self.cur_file_data).add((self.last_line, -first))
290
299
 
291
300
  # Leaving this function, pop the filename stack.
292
301
  self.cur_file_data, self.cur_file_name, self.last_line, self.started_context = (
@@ -296,7 +305,8 @@ class PyTracer(Tracer):
296
305
  if self.started_context:
297
306
  assert self.switch_context is not None
298
307
  self.context = None
299
- self.switch_context(None) # pylint: disable=not-callable
308
+ self.switch_context(None) # pylint: disable=not-callable
309
+
300
310
  return self._cached_bound_method_trace
301
311
 
302
312
  def start(self) -> TTraceFn:
@@ -330,22 +340,19 @@ class PyTracer(Tracer):
330
340
  # Called on a different thread than started us: we can't unhook
331
341
  # ourselves, but we've set the flag that we should stop, so we
332
342
  # won't do any more tracing.
333
- #self.log("~", "stopping on different threads")
343
+ # self.log("~", "stopping on different threads")
334
344
  return
335
345
 
336
346
  # PyPy clears the trace function before running atexit functions,
337
347
  # so don't warn if we are in atexit on PyPy and the trace function
338
348
  # has changed to None. Metacoverage also messes this up, so don't
339
349
  # warn if we are measuring ourselves.
340
- suppress_warning = (
341
- (env.PYPY and self.in_atexit and tf is None)
342
- or env.METACOV
343
- )
350
+ suppress_warning = (env.PYPY and self.in_atexit and tf is None) or env.METACOV
344
351
  if self.warn and not suppress_warning:
345
- if tf != self._cached_bound_method_trace: # pylint: disable=comparison-with-callable
352
+ if tf != self._cached_bound_method_trace: # pylint: disable=comparison-with-callable
346
353
  self.warn(
347
- "Trace function changed, data is likely wrong: " +
348
- f"{tf!r} != {self._cached_bound_method_trace!r}",
354
+ "Trace function changed, data is likely wrong: "
355
+ + f"{tf!r} != {self._cached_bound_method_trace!r}",
349
356
  slug="trace-changed",
350
357
  )
351
358
 
coverage/regions.py CHANGED
@@ -7,7 +7,6 @@ from __future__ import annotations
7
7
 
8
8
  import ast
9
9
  import dataclasses
10
-
11
10
  from typing import cast
12
11
 
13
12
  from coverage.plugin import CodeRegion
@@ -16,6 +15,7 @@ from coverage.plugin import CodeRegion
16
15
  @dataclasses.dataclass
17
16
  class Context:
18
17
  """The nested named context of a function or class."""
18
+
19
19
  name: str
20
20
  kind: str
21
21
  lines: set[int]
@@ -28,6 +28,7 @@ class RegionFinder:
28
28
  attribute.
29
29
 
30
30
  """
31
+
31
32
  def __init__(self) -> None:
32
33
  self.regions: list[CodeRegion] = []
33
34
  self.context: list[Context] = []
coverage/report.py CHANGED
@@ -6,12 +6,11 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  import sys
9
-
10
- from typing import Any, IO, TYPE_CHECKING
11
9
  from collections.abc import Iterable
10
+ from typing import IO, TYPE_CHECKING, Any
12
11
 
13
12
  from coverage.exceptions import ConfigError, NoDataError
14
- from coverage.misc import human_sorted_items
13
+ from coverage.misc import human_sorted_items, plural
15
14
  from coverage.plugin import FileReporter
16
15
  from coverage.report_core import get_analysis_to_report
17
16
  from coverage.results import Analysis, Numbers
@@ -32,7 +31,7 @@ class SummaryReporter:
32
31
  self.output_format = self.config.format or "text"
33
32
  if self.output_format not in {"text", "markdown", "total"}:
34
33
  raise ConfigError(f"Unknown report format choice: {self.output_format!r}")
35
- self.fr_analysis: list[tuple[FileReporter, Analysis]] = []
34
+ self.fr_analyses: list[tuple[FileReporter, Analysis]] = []
36
35
  self.skipped_count = 0
37
36
  self.empty_count = 0
38
37
  self.total = Numbers(precision=self.config.precision)
@@ -47,7 +46,7 @@ class SummaryReporter:
47
46
  """Write a list of strings, joined together."""
48
47
  self.write("".join(items))
49
48
 
50
- def _report_text(
49
+ def report_text(
51
50
  self,
52
51
  header: list[str],
53
52
  lines_values: list[list[Any]],
@@ -75,10 +74,7 @@ class SummaryReporter:
75
74
  Cover="{:>{n}}",
76
75
  Missing="{:>10}",
77
76
  )
78
- header_items = [
79
- formats[item].format(item, name_len=max_name, n=max_n)
80
- for item in header
81
- ]
77
+ header_items = [formats[item].format(item, name_len=max_name, n=max_n) for item in header]
82
78
  header_str = "".join(header_items)
83
79
  rule = "-" * len(header_str)
84
80
 
@@ -86,29 +82,36 @@ class SummaryReporter:
86
82
  self.write(header_str)
87
83
  self.write(rule)
88
84
 
89
- formats.update(dict(Cover="{:>{n}}%"), Missing=" {:9}")
85
+ # Write the data lines
86
+ formats.update(
87
+ dict(
88
+ Cover="{:>{n}}%",
89
+ Missing=" {:9}",
90
+ )
91
+ )
90
92
  for values in lines_values:
91
- # build string with line values
92
- line_items = [
93
- formats[item].format(str(value),
94
- name_len=max_name, n=max_n-1) for item, value in zip(header, values)
95
- ]
96
- self.write_items(line_items)
93
+ self.write_items(
94
+ (
95
+ formats[item].format(str(value), name_len=max_name, n=max_n - 1)
96
+ for item, value in zip(header, values)
97
+ )
98
+ )
97
99
 
98
100
  # Write a TOTAL line
99
101
  if lines_values:
100
102
  self.write(rule)
101
103
 
102
- line_items = [
103
- formats[item].format(str(value),
104
- name_len=max_name, n=max_n-1) for item, value in zip(header, total_line)
105
- ]
106
- self.write_items(line_items)
104
+ self.write_items(
105
+ (
106
+ formats[item].format(str(value), name_len=max_name, n=max_n - 1)
107
+ for item, value in zip(header, total_line)
108
+ )
109
+ )
107
110
 
108
111
  for end_line in end_lines:
109
112
  self.write(end_line)
110
113
 
111
- def _report_markdown(
114
+ def report_markdown(
112
115
  self,
113
116
  header: list[str],
114
117
  lines_values: list[list[Any]],
@@ -138,25 +141,38 @@ class SummaryReporter:
138
141
  max_n = max(len(total_line[header.index("Cover")]) + 6, len(" Cover "))
139
142
  header_items = [formats[item].format(item, name_len=max_name, n=max_n) for item in header]
140
143
  header_str = "".join(header_items)
141
- rule_str = "|" + " ".join(["- |".rjust(len(header_items[0])-1, "-")] +
142
- ["-: |".rjust(len(item)-1, "-") for item in header_items[1:]],
144
+ rule_str = "|" + " ".join(
145
+ ["- |".rjust(len(header_items[0]) - 1, "-")]
146
+ + ["-: |".rjust(len(item) - 1, "-") for item in header_items[1:]],
143
147
  )
144
148
 
145
149
  # Write the header
146
150
  self.write(header_str)
147
151
  self.write(rule_str)
148
152
 
153
+ # Write the data lines
149
154
  for values in lines_values:
150
- # build string with line values
151
- formats.update(dict(Cover="{:>{n}}% |"))
152
- line_items = [
153
- formats[item].format(str(value).replace("_", "\\_"), name_len=max_name, n=max_n-1)
154
- for item, value in zip(header, values)
155
- ]
156
- self.write_items(line_items)
155
+ formats.update(
156
+ dict(
157
+ Cover="{:>{n}}% |",
158
+ )
159
+ )
160
+ self.write_items(
161
+ (
162
+ formats[item].format(
163
+ str(value).replace("_", "\\_"), name_len=max_name, n=max_n - 1
164
+ )
165
+ for item, value in zip(header, values)
166
+ )
167
+ )
157
168
 
158
169
  # Write the TOTAL line
159
- formats.update(dict(Name="|{:>{name_len}} |", Cover="{:>{n}} |"))
170
+ formats.update(
171
+ dict(
172
+ Name="|{:>{name_len}} |",
173
+ Cover="{:>{n}} |",
174
+ ),
175
+ )
160
176
  total_line_items: list[str] = []
161
177
  for item, value in zip(header, total_line):
162
178
  if value == "":
@@ -167,6 +183,7 @@ class SummaryReporter:
167
183
  insert = f" **{value}**"
168
184
  total_line_items += formats[item].format(insert, name_len=max_name, n=max_n)
169
185
  self.write_items(total_line_items)
186
+
170
187
  for end_line in end_lines:
171
188
  self.write(end_line)
172
189
 
@@ -209,9 +226,8 @@ class SummaryReporter:
209
226
  # `lines_values` is list of lists of sortable values.
210
227
  lines_values = []
211
228
 
212
- for (fr, analysis) in self.fr_analysis:
229
+ for fr, analysis in self.fr_analyses:
213
230
  nums = analysis.numbers
214
-
215
231
  args = [fr.relative_filename(), nums.n_statements, nums.n_missing]
216
232
  if self.branches:
217
233
  args += [nums.n_branches, nums.n_partial_branches]
@@ -251,18 +267,18 @@ class SummaryReporter:
251
267
  # Create other final lines.
252
268
  end_lines = []
253
269
  if self.config.skip_covered and self.skipped_count:
254
- file_suffix = "s" if self.skipped_count>1 else ""
270
+ files = plural(self.skipped_count, "file")
255
271
  end_lines.append(
256
- f"\n{self.skipped_count} file{file_suffix} skipped due to complete coverage.",
272
+ f"\n{self.skipped_count} {files} skipped due to complete coverage.",
257
273
  )
258
274
  if self.config.skip_empty and self.empty_count:
259
- file_suffix = "s" if self.empty_count > 1 else ""
260
- end_lines.append(f"\n{self.empty_count} empty file{file_suffix} skipped.")
275
+ files = plural(self.empty_count, "file")
276
+ end_lines.append(f"\n{self.empty_count} empty {files} skipped.")
261
277
 
262
278
  if self.output_format == "markdown":
263
- formatter = self._report_markdown
279
+ formatter = self.report_markdown
264
280
  else:
265
- formatter = self._report_text
281
+ formatter = self.report_text
266
282
  formatter(header, lines_values, total_line, end_lines)
267
283
 
268
284
  def report_one_file(self, fr: FileReporter, analysis: Analysis) -> None:
@@ -270,8 +286,8 @@ class SummaryReporter:
270
286
  nums = analysis.numbers
271
287
  self.total += nums
272
288
 
273
- no_missing_lines = (nums.n_missing == 0)
274
- no_missing_branches = (nums.n_partial_branches == 0)
289
+ no_missing_lines = (nums.n_missing == 0) # fmt: skip
290
+ no_missing_branches = (nums.n_partial_branches == 0) # fmt: skip
275
291
  if self.config.skip_covered and no_missing_lines and no_missing_branches:
276
292
  # Don't report on 100% files.
277
293
  self.skipped_count += 1
@@ -279,4 +295,4 @@ class SummaryReporter:
279
295
  # Don't report on empty files.
280
296
  self.empty_count += 1
281
297
  else:
282
- self.fr_analysis.append((fr, analysis))
298
+ self.fr_analyses.append((fr, analysis))
coverage/report_core.py CHANGED
@@ -6,14 +6,11 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  import sys
9
-
10
- from typing import (
11
- Callable, IO, Protocol, TYPE_CHECKING,
12
- )
13
- from collections.abc import Iterable, Iterator
9
+ from collections.abc import Iterable
10
+ from typing import IO, TYPE_CHECKING, Callable, Protocol
14
11
 
15
12
  from coverage.exceptions import NoDataError, NotPython
16
- from coverage.files import prep_patterns, GlobMatcher
13
+ from coverage.files import GlobMatcher, prep_patterns
17
14
  from coverage.misc import ensure_dir_for_file, file_be_gone
18
15
  from coverage.plugin import FileReporter
19
16
  from coverage.results import Analysis
@@ -68,13 +65,13 @@ def render_report(
68
65
  if file_to_close is not None:
69
66
  file_to_close.close()
70
67
  if delete_file:
71
- file_be_gone(output_path) # pragma: part covered (doesn't return)
68
+ file_be_gone(output_path) # pragma: part covered (doesn't return)
72
69
 
73
70
 
74
71
  def get_analysis_to_report(
75
72
  coverage: Coverage,
76
73
  morfs: Iterable[TMorf] | None,
77
- ) -> Iterator[tuple[FileReporter, Analysis]]:
74
+ ) -> Iterable[tuple[FileReporter, Analysis]]:
78
75
  """Get the files to report on.
79
76
 
80
77
  For each morf in `morfs`, if it should be reported on (based on the omit
@@ -104,7 +101,7 @@ def get_analysis_to_report(
104
101
  # explicitly suppress those errors.
105
102
  # NotPython is only raised by PythonFileReporter, which has a
106
103
  # should_be_python() method.
107
- if fr.should_be_python(): # type: ignore[attr-defined]
104
+ if fr.should_be_python(): # type: ignore[attr-defined]
108
105
  if config.ignore_errors:
109
106
  msg = f"Couldn't parse Python file '{fr.filename}'"
110
107
  coverage._warn(msg, slug="couldnt-parse")