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.
- coverage/__init__.py +3 -1
- coverage/__main__.py +3 -1
- coverage/annotate.py +2 -3
- coverage/bytecode.py +178 -4
- coverage/cmdline.py +330 -155
- coverage/collector.py +32 -43
- coverage/config.py +167 -63
- coverage/context.py +5 -6
- coverage/control.py +165 -86
- coverage/core.py +71 -34
- coverage/data.py +4 -5
- coverage/debug.py +113 -57
- coverage/disposition.py +2 -1
- coverage/env.py +29 -78
- coverage/exceptions.py +29 -7
- coverage/execfile.py +19 -14
- coverage/files.py +24 -19
- coverage/html.py +118 -75
- coverage/htmlfiles/coverage_html.js +12 -10
- coverage/htmlfiles/index.html +45 -10
- coverage/htmlfiles/pyfile.html +2 -2
- coverage/htmlfiles/style.css +54 -6
- coverage/htmlfiles/style.scss +85 -3
- coverage/inorout.py +62 -45
- coverage/jsonreport.py +22 -9
- coverage/lcovreport.py +16 -18
- coverage/misc.py +51 -47
- coverage/multiproc.py +12 -7
- coverage/numbits.py +4 -5
- coverage/parser.py +150 -251
- coverage/patch.py +166 -0
- coverage/phystokens.py +25 -26
- coverage/plugin.py +14 -14
- coverage/plugin_support.py +37 -36
- coverage/python.py +13 -14
- coverage/pytracer.py +31 -33
- coverage/regions.py +3 -2
- coverage/report.py +60 -44
- coverage/report_core.py +7 -10
- coverage/results.py +152 -68
- coverage/sqldata.py +261 -211
- coverage/sqlitedb.py +37 -29
- coverage/sysmon.py +237 -162
- coverage/templite.py +19 -7
- coverage/tomlconfig.py +13 -13
- coverage/tracer.cpython-312-aarch64-linux-musl.so +0 -0
- coverage/tracer.pyi +3 -1
- coverage/types.py +26 -23
- coverage/version.py +4 -19
- coverage/xmlreport.py +17 -14
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/METADATA +50 -28
- coverage-7.12.0.dist-info/RECORD +59 -0
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/WHEEL +1 -1
- coverage-7.6.10.dist-info/RECORD +0 -58
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/entry_points.txt +0 -0
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info/licenses}/LICENSE.txt +0 -0
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/top_level.txt +0 -0
coverage/pytracer.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/
|
|
2
|
+
# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
|
|
3
3
|
|
|
4
4
|
"""Raw data collector for coverage.py."""
|
|
5
5
|
|
|
@@ -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,20 +18,19 @@ 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
|
|
|
31
|
-
|
|
32
30
|
# I don't understand why, but if we use `cast(set[TLineNo], ...)` inside
|
|
33
31
|
# the _trace() function, we get some strange behavior on PyPy 3.10.
|
|
34
32
|
# Assigning these names here and using them below fixes the problem.
|
|
35
|
-
# See https://github.com/
|
|
33
|
+
# See https://github.com/coveragepy/coveragepy/issues/1902
|
|
36
34
|
set_TLineNo = set[TLineNo]
|
|
37
35
|
set_TArc = set[TArc]
|
|
38
36
|
|
|
@@ -53,6 +51,7 @@ else:
|
|
|
53
51
|
|
|
54
52
|
THIS_FILE = __file__.rstrip("co")
|
|
55
53
|
|
|
54
|
+
|
|
56
55
|
class PyTracer(Tracer):
|
|
57
56
|
"""Python implementation of the raw data tracer."""
|
|
58
57
|
|
|
@@ -126,19 +125,20 @@ class PyTracer(Tracer):
|
|
|
126
125
|
|
|
127
126
|
def log(self, marker: str, *args: Any) -> None:
|
|
128
127
|
"""For hard-core logging of what this tracer is doing."""
|
|
129
|
-
with open("/tmp/debug_trace.txt", "a") as f:
|
|
128
|
+
with open("/tmp/debug_trace.txt", "a", encoding="utf-8") as f:
|
|
130
129
|
f.write(f"{marker} {self.id}[{len(self.data_stack)}]")
|
|
131
|
-
if 0:
|
|
132
|
-
f.write(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
+
)
|
|
136
137
|
f.write(" {}".format(" ".join(map(str, args))))
|
|
137
|
-
if 0:
|
|
138
|
-
f.write(" | ")
|
|
138
|
+
if 0: # if you want callers..
|
|
139
|
+
f.write(" | ") # type: ignore[unreachable]
|
|
139
140
|
stack = " / ".join(
|
|
140
|
-
(fname or "???").rpartition("/")[-1]
|
|
141
|
-
for _, fname, _, _ in self.data_stack
|
|
141
|
+
(fname or "???").rpartition("/")[-1] for _, fname, _, _ in self.data_stack
|
|
142
142
|
)
|
|
143
143
|
f.write(stack)
|
|
144
144
|
f.write("\n")
|
|
@@ -147,8 +147,8 @@ class PyTracer(Tracer):
|
|
|
147
147
|
self,
|
|
148
148
|
frame: FrameType,
|
|
149
149
|
event: str,
|
|
150
|
-
arg: Any,
|
|
151
|
-
lineno: TLineNo | None = None,
|
|
150
|
+
arg: Any, # pylint: disable=unused-argument
|
|
151
|
+
lineno: TLineNo | None = None, # pylint: disable=unused-argument
|
|
152
152
|
) -> TTraceFn | None:
|
|
153
153
|
"""The trace function passed to sys.settrace."""
|
|
154
154
|
|
|
@@ -158,11 +158,11 @@ class PyTracer(Tracer):
|
|
|
158
158
|
# f = frame; code = f.f_code
|
|
159
159
|
# self.log(":", f"{code.co_filename} {f.f_lineno} {code.co_name}()", event)
|
|
160
160
|
|
|
161
|
-
if
|
|
161
|
+
if self.stopped and sys.gettrace() == self._cached_bound_method_trace: # pylint: disable=comparison-with-callable
|
|
162
162
|
# The PyTrace.stop() method has been called, possibly by another
|
|
163
163
|
# thread, let's deactivate ourselves now.
|
|
164
164
|
if 0:
|
|
165
|
-
f = frame
|
|
165
|
+
f = frame # type: ignore[unreachable]
|
|
166
166
|
self.log("---\nX", f.f_code.co_filename, f.f_lineno)
|
|
167
167
|
while f:
|
|
168
168
|
self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace)
|
|
@@ -187,12 +187,12 @@ class PyTracer(Tracer):
|
|
|
187
187
|
if event == "call":
|
|
188
188
|
# Should we start a new context?
|
|
189
189
|
if self.should_start_context and self.context is None:
|
|
190
|
-
context_maybe = self.should_start_context(frame)
|
|
190
|
+
context_maybe = self.should_start_context(frame) # pylint: disable=not-callable
|
|
191
191
|
if context_maybe is not None:
|
|
192
192
|
self.context = context_maybe
|
|
193
193
|
started_context = True
|
|
194
194
|
assert self.switch_context is not None
|
|
195
|
-
self.switch_context(self.context)
|
|
195
|
+
self.switch_context(self.context) # pylint: disable=not-callable
|
|
196
196
|
else:
|
|
197
197
|
started_context = False
|
|
198
198
|
else:
|
|
@@ -248,9 +248,9 @@ class PyTracer(Tracer):
|
|
|
248
248
|
# The current opcode is guaranteed to be RESUME. The argument
|
|
249
249
|
# determines what kind of resume it is.
|
|
250
250
|
oparg = frame.f_code.co_code[frame.f_lasti + 1]
|
|
251
|
-
real_call = (oparg == 0)
|
|
251
|
+
real_call = (oparg == 0) # fmt: skip
|
|
252
252
|
else:
|
|
253
|
-
real_call = (getattr(frame, "f_lasti", -1) < 0)
|
|
253
|
+
real_call = (getattr(frame, "f_lasti", -1) < 0) # fmt: skip
|
|
254
254
|
if real_call:
|
|
255
255
|
self.last_line = -frame.f_code.co_firstlineno
|
|
256
256
|
else:
|
|
@@ -281,7 +281,7 @@ class PyTracer(Tracer):
|
|
|
281
281
|
# It is a real return if we aren't going to resume next.
|
|
282
282
|
if env.PYBEHAVIOR.lasti_is_yield:
|
|
283
283
|
lasti += 2
|
|
284
|
-
real_return =
|
|
284
|
+
real_return = code[lasti] != RESUME
|
|
285
285
|
else:
|
|
286
286
|
if code[lasti] == RETURN_VALUE:
|
|
287
287
|
real_return = True
|
|
@@ -305,7 +305,8 @@ class PyTracer(Tracer):
|
|
|
305
305
|
if self.started_context:
|
|
306
306
|
assert self.switch_context is not None
|
|
307
307
|
self.context = None
|
|
308
|
-
self.switch_context(None)
|
|
308
|
+
self.switch_context(None) # pylint: disable=not-callable
|
|
309
|
+
|
|
309
310
|
return self._cached_bound_method_trace
|
|
310
311
|
|
|
311
312
|
def start(self) -> TTraceFn:
|
|
@@ -339,22 +340,19 @@ class PyTracer(Tracer):
|
|
|
339
340
|
# Called on a different thread than started us: we can't unhook
|
|
340
341
|
# ourselves, but we've set the flag that we should stop, so we
|
|
341
342
|
# won't do any more tracing.
|
|
342
|
-
#self.log("~", "stopping on different threads")
|
|
343
|
+
# self.log("~", "stopping on different threads")
|
|
343
344
|
return
|
|
344
345
|
|
|
345
346
|
# PyPy clears the trace function before running atexit functions,
|
|
346
347
|
# so don't warn if we are in atexit on PyPy and the trace function
|
|
347
348
|
# has changed to None. Metacoverage also messes this up, so don't
|
|
348
349
|
# warn if we are measuring ourselves.
|
|
349
|
-
suppress_warning = (
|
|
350
|
-
(env.PYPY and self.in_atexit and tf is None)
|
|
351
|
-
or env.METACOV
|
|
352
|
-
)
|
|
350
|
+
suppress_warning = (env.PYPY and self.in_atexit and tf is None) or env.METACOV
|
|
353
351
|
if self.warn and not suppress_warning:
|
|
354
|
-
if tf != self._cached_bound_method_trace:
|
|
352
|
+
if tf != self._cached_bound_method_trace: # pylint: disable=comparison-with-callable
|
|
355
353
|
self.warn(
|
|
356
|
-
"Trace function changed, data is likely wrong: "
|
|
357
|
-
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}",
|
|
358
356
|
slug="trace-changed",
|
|
359
357
|
)
|
|
360
358
|
|
coverage/regions.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/
|
|
2
|
+
# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
|
|
3
3
|
|
|
4
4
|
"""Find functions and classes in Python code."""
|
|
5
5
|
|
|
@@ -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
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
|
|
2
|
-
# For details: https://github.com/
|
|
2
|
+
# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
|
|
3
3
|
|
|
4
4
|
"""Summary reporting"""
|
|
5
5
|
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
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(
|
|
142
|
-
["
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
270
|
+
files = plural(self.skipped_count, "file")
|
|
255
271
|
end_lines.append(
|
|
256
|
-
f"\n{self.skipped_count}
|
|
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
|
-
|
|
260
|
-
end_lines.append(f"\n{self.empty_count} empty
|
|
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.
|
|
279
|
+
formatter = self.report_markdown
|
|
264
280
|
else:
|
|
265
|
-
formatter = self.
|
|
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.
|
|
298
|
+
self.fr_analyses.append((fr, analysis))
|
coverage/report_core.py
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
|
|
2
|
-
# For details: https://github.com/
|
|
2
|
+
# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
|
|
3
3
|
|
|
4
4
|
"""Reporter foundation for coverage.py."""
|
|
5
5
|
|
|
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
|
|
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)
|
|
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
|
-
) ->
|
|
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():
|
|
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")
|