coverage 7.13.1__cp314-cp314t-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 (61) hide show
  1. a1_coverage.pth +1 -0
  2. coverage/__init__.py +38 -0
  3. coverage/__main__.py +12 -0
  4. coverage/annotate.py +113 -0
  5. coverage/bytecode.py +197 -0
  6. coverage/cmdline.py +1220 -0
  7. coverage/collector.py +487 -0
  8. coverage/config.py +732 -0
  9. coverage/context.py +74 -0
  10. coverage/control.py +1514 -0
  11. coverage/core.py +139 -0
  12. coverage/data.py +251 -0
  13. coverage/debug.py +669 -0
  14. coverage/disposition.py +59 -0
  15. coverage/env.py +135 -0
  16. coverage/exceptions.py +85 -0
  17. coverage/execfile.py +329 -0
  18. coverage/files.py +553 -0
  19. coverage/html.py +860 -0
  20. coverage/htmlfiles/coverage_html.js +735 -0
  21. coverage/htmlfiles/favicon_32.png +0 -0
  22. coverage/htmlfiles/index.html +199 -0
  23. coverage/htmlfiles/keybd_closed.png +0 -0
  24. coverage/htmlfiles/pyfile.html +149 -0
  25. coverage/htmlfiles/style.css +389 -0
  26. coverage/htmlfiles/style.scss +844 -0
  27. coverage/inorout.py +590 -0
  28. coverage/jsonreport.py +200 -0
  29. coverage/lcovreport.py +218 -0
  30. coverage/misc.py +381 -0
  31. coverage/multiproc.py +120 -0
  32. coverage/numbits.py +146 -0
  33. coverage/parser.py +1215 -0
  34. coverage/patch.py +118 -0
  35. coverage/phystokens.py +197 -0
  36. coverage/plugin.py +617 -0
  37. coverage/plugin_support.py +299 -0
  38. coverage/pth_file.py +16 -0
  39. coverage/py.typed +1 -0
  40. coverage/python.py +272 -0
  41. coverage/pytracer.py +370 -0
  42. coverage/regions.py +127 -0
  43. coverage/report.py +298 -0
  44. coverage/report_core.py +117 -0
  45. coverage/results.py +502 -0
  46. coverage/sqldata.py +1212 -0
  47. coverage/sqlitedb.py +226 -0
  48. coverage/sysmon.py +509 -0
  49. coverage/templite.py +319 -0
  50. coverage/tomlconfig.py +212 -0
  51. coverage/tracer.cpython-314t-aarch64-linux-musl.so +0 -0
  52. coverage/tracer.pyi +43 -0
  53. coverage/types.py +214 -0
  54. coverage/version.py +35 -0
  55. coverage/xmlreport.py +263 -0
  56. coverage-7.13.1.dist-info/METADATA +200 -0
  57. coverage-7.13.1.dist-info/RECORD +61 -0
  58. coverage-7.13.1.dist-info/WHEEL +5 -0
  59. coverage-7.13.1.dist-info/entry_points.txt +4 -0
  60. coverage-7.13.1.dist-info/licenses/LICENSE.txt +177 -0
  61. coverage-7.13.1.dist-info/top_level.txt +1 -0
coverage/report.py ADDED
@@ -0,0 +1,298 @@
1
+ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2
+ # For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
3
+
4
+ """Summary reporting"""
5
+
6
+ from __future__ import annotations
7
+
8
+ import sys
9
+ from collections.abc import Iterable
10
+ from typing import IO, TYPE_CHECKING, Any
11
+
12
+ from coverage.exceptions import ConfigError, NoDataError
13
+ from coverage.misc import human_sorted_items, plural
14
+ from coverage.plugin import FileReporter
15
+ from coverage.report_core import get_analysis_to_report
16
+ from coverage.results import Analysis, Numbers
17
+ from coverage.types import TMorfs
18
+
19
+ if TYPE_CHECKING:
20
+ from coverage import Coverage
21
+
22
+
23
+ class SummaryReporter:
24
+ """A reporter for writing the summary report."""
25
+
26
+ def __init__(self, coverage: Coverage) -> None:
27
+ self.coverage = coverage
28
+ self.config = self.coverage.config
29
+ self.branches = coverage.get_data().has_arcs()
30
+ self.outfile: IO[str] | None = None
31
+ self.output_format = self.config.format or "text"
32
+ if self.output_format not in {"text", "markdown", "total"}:
33
+ raise ConfigError(f"Unknown report format choice: {self.output_format!r}")
34
+ self.fr_analyses: list[tuple[FileReporter, Analysis]] = []
35
+ self.skipped_count = 0
36
+ self.empty_count = 0
37
+ self.total = Numbers(precision=self.config.precision)
38
+
39
+ def write(self, line: str) -> None:
40
+ """Write a line to the output, adding a newline."""
41
+ assert self.outfile is not None
42
+ self.outfile.write(line.rstrip())
43
+ self.outfile.write("\n")
44
+
45
+ def write_items(self, items: Iterable[str]) -> None:
46
+ """Write a list of strings, joined together."""
47
+ self.write("".join(items))
48
+
49
+ def report_text(
50
+ self,
51
+ header: list[str],
52
+ lines_values: list[list[Any]],
53
+ total_line: list[Any],
54
+ end_lines: list[str],
55
+ ) -> None:
56
+ """Internal method that prints report data in text format.
57
+
58
+ `header` is a list with captions.
59
+ `lines_values` is list of lists of sortable values.
60
+ `total_line` is a list with values of the total line.
61
+ `end_lines` is a list of ending lines with information about skipped files.
62
+
63
+ """
64
+ # Prepare the formatting strings, header, and column sorting.
65
+ max_name = max([len(line[0]) for line in lines_values] + [5]) + 1
66
+ max_n = max(len(total_line[header.index("Cover")]) + 2, len(" Cover")) + 1
67
+ max_n = max([max_n] + [len(line[header.index("Cover")]) + 2 for line in lines_values])
68
+ formats = dict(
69
+ Name="{:{name_len}}",
70
+ Stmts="{:>7}",
71
+ Miss="{:>7}",
72
+ Branch="{:>7}",
73
+ BrPart="{:>7}",
74
+ Cover="{:>{n}}",
75
+ Missing="{:>10}",
76
+ )
77
+ header_items = [formats[item].format(item, name_len=max_name, n=max_n) for item in header]
78
+ header_str = "".join(header_items)
79
+ rule = "-" * len(header_str)
80
+
81
+ # Write the header
82
+ self.write(header_str)
83
+ self.write(rule)
84
+
85
+ # Write the data lines
86
+ formats.update(
87
+ dict(
88
+ Cover="{:>{n}}%",
89
+ Missing=" {:9}",
90
+ )
91
+ )
92
+ for values in lines_values:
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
+ )
99
+
100
+ # Write a TOTAL line
101
+ if lines_values:
102
+ self.write(rule)
103
+
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
+ )
110
+
111
+ for end_line in end_lines:
112
+ self.write(end_line)
113
+
114
+ def report_markdown(
115
+ self,
116
+ header: list[str],
117
+ lines_values: list[list[Any]],
118
+ total_line: list[Any],
119
+ end_lines: list[str],
120
+ ) -> None:
121
+ """Internal method that prints report data in markdown format.
122
+
123
+ `header` is a list with captions.
124
+ `lines_values` is a sorted list of lists containing coverage information.
125
+ `total_line` is a list with values of the total line.
126
+ `end_lines` is a list of ending lines with information about skipped files.
127
+
128
+ """
129
+ # Prepare the formatting strings, header, and column sorting.
130
+ max_name = max((len(line[0].replace("_", "\\_")) for line in lines_values), default=0)
131
+ max_name = max(max_name, len("**TOTAL**")) + 1
132
+ formats = dict(
133
+ Name="| {:{name_len}}|",
134
+ Stmts="{:>9} |",
135
+ Miss="{:>9} |",
136
+ Branch="{:>9} |",
137
+ BrPart="{:>9} |",
138
+ Cover="{:>{n}} |",
139
+ Missing="{:>10} |",
140
+ )
141
+ max_n = max(len(total_line[header.index("Cover")]) + 6, len(" Cover "))
142
+ header_items = [formats[item].format(item, name_len=max_name, n=max_n) for item in header]
143
+ header_str = "".join(header_items)
144
+ rule_str = "|" + " ".join(
145
+ ["- |".rjust(len(header_items[0]) - 1, "-")]
146
+ + ["-: |".rjust(len(item) - 1, "-") for item in header_items[1:]],
147
+ )
148
+
149
+ # Write the header
150
+ self.write(header_str)
151
+ self.write(rule_str)
152
+
153
+ # Write the data lines
154
+ for values in lines_values:
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
+ )
168
+
169
+ # Write the TOTAL line
170
+ formats.update(
171
+ dict(
172
+ Name="|{:{name_len}} |",
173
+ Cover="{:>{n}} |",
174
+ ),
175
+ )
176
+ total_line_items: list[str] = []
177
+ for item, value in zip(header, total_line):
178
+ if value == "":
179
+ insert = value
180
+ elif item == "Cover":
181
+ insert = f" **{value}%**"
182
+ else:
183
+ insert = f" **{value}**"
184
+ total_line_items += formats[item].format(insert, name_len=max_name, n=max_n)
185
+ self.write_items(total_line_items)
186
+
187
+ for end_line in end_lines:
188
+ self.write(end_line)
189
+
190
+ def report(self, morfs: TMorfs, outfile: IO[str] | None = None) -> float:
191
+ """Writes a report summarizing coverage statistics per module.
192
+
193
+ `outfile` is a text-mode file object to write the summary to.
194
+
195
+ """
196
+ self.outfile = outfile or sys.stdout
197
+
198
+ self.coverage.get_data().set_query_contexts(self.config.report_contexts)
199
+ for fr, analysis in get_analysis_to_report(self.coverage, morfs):
200
+ self.report_one_file(fr, analysis)
201
+
202
+ if not self.total.n_files and not self.skipped_count:
203
+ raise NoDataError("No data to report.")
204
+
205
+ if self.output_format == "total":
206
+ self.write(self.total.pc_covered_str)
207
+ else:
208
+ self.tabular_report()
209
+
210
+ return self.total.pc_covered
211
+
212
+ def tabular_report(self) -> None:
213
+ """Writes tabular report formats."""
214
+ # Prepare the header line and column sorting.
215
+ header = ["Name", "Stmts", "Miss"]
216
+ if self.branches:
217
+ header += ["Branch", "BrPart"]
218
+ header += ["Cover"]
219
+ if self.config.show_missing:
220
+ header += ["Missing"]
221
+
222
+ column_order = dict(name=0, stmts=1, miss=2, cover=-1)
223
+ if self.branches:
224
+ column_order.update(dict(branch=3, brpart=4))
225
+
226
+ # `lines_values` is list of lists of sortable values.
227
+ lines_values = []
228
+
229
+ for fr, analysis in self.fr_analyses:
230
+ nums = analysis.numbers
231
+ args = [fr.relative_filename(), nums.n_statements, nums.n_missing]
232
+ if self.branches:
233
+ args += [nums.n_branches, nums.n_partial_branches]
234
+ args += [nums.pc_covered_str]
235
+ if self.config.show_missing:
236
+ args += [analysis.missing_formatted(branches=True)]
237
+ args += [nums.pc_covered]
238
+ lines_values.append(args)
239
+
240
+ # Line sorting.
241
+ sort_option = (self.config.sort or "name").lower()
242
+ reverse = False
243
+ if sort_option[0] == "-":
244
+ reverse = True
245
+ sort_option = sort_option[1:]
246
+ elif sort_option[0] == "+":
247
+ sort_option = sort_option[1:]
248
+ sort_idx = column_order.get(sort_option)
249
+ if sort_idx is None:
250
+ raise ConfigError(f"Invalid sorting option: {self.config.sort!r}")
251
+ if sort_option == "name":
252
+ lines_values = human_sorted_items(lines_values, reverse=reverse)
253
+ else:
254
+ lines_values.sort(
255
+ key=lambda line: (line[sort_idx], line[0]),
256
+ reverse=reverse,
257
+ )
258
+
259
+ # Calculate total if we had at least one file.
260
+ total_line = ["TOTAL", self.total.n_statements, self.total.n_missing]
261
+ if self.branches:
262
+ total_line += [self.total.n_branches, self.total.n_partial_branches]
263
+ total_line += [self.total.pc_covered_str]
264
+ if self.config.show_missing:
265
+ total_line += [""]
266
+
267
+ # Create other final lines.
268
+ end_lines = []
269
+ if self.config.skip_covered and self.skipped_count:
270
+ files = plural(self.skipped_count, "file")
271
+ end_lines.append(
272
+ f"\n{self.skipped_count} {files} skipped due to complete coverage.",
273
+ )
274
+ if self.config.skip_empty and self.empty_count:
275
+ files = plural(self.empty_count, "file")
276
+ end_lines.append(f"\n{self.empty_count} empty {files} skipped.")
277
+
278
+ if self.output_format == "markdown":
279
+ formatter = self.report_markdown
280
+ else:
281
+ formatter = self.report_text
282
+ formatter(header, lines_values, total_line, end_lines)
283
+
284
+ def report_one_file(self, fr: FileReporter, analysis: Analysis) -> None:
285
+ """Report on just one file, the callback from report()."""
286
+ nums = analysis.numbers
287
+ self.total += nums
288
+
289
+ no_missing_lines = (nums.n_missing == 0) # fmt: skip
290
+ no_missing_branches = (nums.n_partial_branches == 0) # fmt: skip
291
+ if self.config.skip_covered and no_missing_lines and no_missing_branches:
292
+ # Don't report on 100% files.
293
+ self.skipped_count += 1
294
+ elif self.config.skip_empty and nums.n_statements == 0:
295
+ # Don't report on empty files.
296
+ self.empty_count += 1
297
+ else:
298
+ self.fr_analyses.append((fr, analysis))
@@ -0,0 +1,117 @@
1
+ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2
+ # For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
3
+
4
+ """Reporter foundation for coverage.py."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import sys
9
+ from collections.abc import Callable, Iterable
10
+ from typing import IO, TYPE_CHECKING, Protocol
11
+
12
+ from coverage.exceptions import NoDataError, NotPython
13
+ from coverage.files import GlobMatcher, prep_patterns
14
+ from coverage.misc import ensure_dir_for_file, file_be_gone
15
+ from coverage.plugin import FileReporter
16
+ from coverage.results import Analysis
17
+ from coverage.types import TMorfs
18
+
19
+ if TYPE_CHECKING:
20
+ from coverage import Coverage
21
+
22
+
23
+ class Reporter(Protocol):
24
+ """What we expect of reporters."""
25
+
26
+ report_type: str
27
+
28
+ def report(self, morfs: TMorfs, outfile: IO[str]) -> float:
29
+ """Generate a report of `morfs`, written to `outfile`."""
30
+
31
+
32
+ def render_report(
33
+ output_path: str,
34
+ reporter: Reporter,
35
+ morfs: TMorfs,
36
+ msgfn: Callable[[str], None],
37
+ ) -> float:
38
+ """Run a one-file report generator, managing the output file.
39
+
40
+ This function ensures the output file is ready to be written to. Then writes
41
+ the report to it. Then closes the file and cleans up.
42
+
43
+ """
44
+ file_to_close = None
45
+ delete_file = False
46
+
47
+ if output_path == "-":
48
+ outfile = sys.stdout
49
+ else:
50
+ # Ensure that the output directory is created; done here because this
51
+ # report pre-opens the output file. HtmlReporter does this on its own
52
+ # because its task is more complex, being multiple files.
53
+ ensure_dir_for_file(output_path)
54
+ outfile = open(output_path, "w", encoding="utf-8")
55
+ file_to_close = outfile
56
+ delete_file = True
57
+
58
+ try:
59
+ ret = reporter.report(morfs, outfile=outfile)
60
+ if file_to_close is not None:
61
+ msgfn(f"Wrote {reporter.report_type} to {output_path}")
62
+ delete_file = False
63
+ return ret
64
+ finally:
65
+ if file_to_close is not None:
66
+ file_to_close.close()
67
+ if delete_file:
68
+ file_be_gone(output_path) # pragma: part covered (doesn't return)
69
+
70
+
71
+ def get_analysis_to_report(
72
+ coverage: Coverage,
73
+ morfs: TMorfs,
74
+ ) -> Iterable[tuple[FileReporter, Analysis]]:
75
+ """Get the files to report on.
76
+
77
+ For each morf in `morfs`, if it should be reported on (based on the omit
78
+ and include configuration options), yield a pair, the `FileReporter` and
79
+ `Analysis` for the morf.
80
+
81
+ """
82
+ fr_morfs = coverage._get_file_reporters(morfs)
83
+ config = coverage.config
84
+
85
+ if config.report_include:
86
+ matcher = GlobMatcher(prep_patterns(config.report_include), "report_include")
87
+ fr_morfs = [(fr, morf) for (fr, morf) in fr_morfs if matcher.match(fr.filename)]
88
+
89
+ if config.report_omit:
90
+ matcher = GlobMatcher(prep_patterns(config.report_omit), "report_omit")
91
+ fr_morfs = [(fr, morf) for (fr, morf) in fr_morfs if not matcher.match(fr.filename)]
92
+
93
+ if not fr_morfs:
94
+ raise NoDataError("No data to report.")
95
+
96
+ for fr, morf in sorted(fr_morfs):
97
+ try:
98
+ analysis = coverage._analyze(morf)
99
+ except NotPython:
100
+ # Only report errors for .py files, and only if we didn't
101
+ # explicitly suppress those errors.
102
+ # NotPython is only raised by PythonFileReporter, which has a
103
+ # should_be_python() method.
104
+ if fr.should_be_python(): # type: ignore[attr-defined]
105
+ if config.ignore_errors:
106
+ msg = f"Couldn't parse Python file '{fr.filename}'"
107
+ coverage._warn(msg, slug="couldnt-parse")
108
+ else:
109
+ raise
110
+ except Exception as exc:
111
+ if config.ignore_errors:
112
+ msg = f"Couldn't parse '{fr.filename}': {exc}".rstrip()
113
+ coverage._warn(msg, slug="couldnt-parse")
114
+ else:
115
+ raise
116
+ else:
117
+ yield (fr, analysis)