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/cmdline.py CHANGED
@@ -6,25 +6,25 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  import glob
9
- import optparse # pylint: disable=deprecated-module
9
+ import optparse
10
10
  import os
11
11
  import os.path
12
12
  import shlex
13
+ import signal
13
14
  import sys
14
15
  import textwrap
15
16
  import traceback
16
-
17
- from typing import cast, Any, NoReturn
17
+ import types
18
+ from typing import Any, NoReturn, cast
18
19
 
19
20
  import coverage
20
- from coverage import Coverage
21
- from coverage import env
21
+ from coverage import Coverage, env
22
22
  from coverage.config import CoverageConfig
23
23
  from coverage.control import DEFAULT_DATAFILE
24
- from coverage.core import HAS_CTRACER
25
- from coverage.data import combinable_files, debug_data_file
24
+ from coverage.core import CTRACER_FILE
25
+ from coverage.data import CoverageData, combinable_files, debug_data_file
26
26
  from coverage.debug import info_header, short_stack, write_formatted_info
27
- from coverage.exceptions import _BaseCoverageException, _ExceptionDuringRun, NoSource
27
+ from coverage.exceptions import NoSource, CoverageException, _ExceptionDuringRun
28
28
  from coverage.execfile import PyRunner
29
29
  from coverage.results import display_covered, should_fail_under
30
30
  from coverage.version import __url__
@@ -32,6 +32,12 @@ from coverage.version import __url__
32
32
  # When adding to this file, alphabetization is important. Look for
33
33
  # "alphabetize" comments throughout.
34
34
 
35
+
36
+ def oneline(text: str) -> str:
37
+ """Turn a multi-line string into one line for help to reformat nicely."""
38
+ return " ".join(text.split())
39
+
40
+
35
41
  class Opts:
36
42
  """A namespace class for individual options we'll build parsers from."""
37
43
 
@@ -39,192 +45,324 @@ class Opts:
39
45
  # appears on the command line.
40
46
 
41
47
  append = optparse.make_option(
42
- "-a", "--append", action="store_true",
43
- help="Append coverage data to .coverage, otherwise it starts clean each time.",
48
+ "-a",
49
+ "--append",
50
+ action="store_true",
51
+ help="Append data to the data file. Otherwise it starts clean each time.",
44
52
  )
45
53
  branch = optparse.make_option(
46
- "", "--branch", action="store_true",
54
+ "",
55
+ "--branch",
56
+ action="store_true",
47
57
  help="Measure branch coverage in addition to statement coverage.",
48
58
  )
49
59
  concurrency = optparse.make_option(
50
- "", "--concurrency", action="store", metavar="LIBS",
51
- help=(
52
- "Properly measure code using a concurrency library. " +
53
- "Valid values are: {}, or a comma-list of them."
60
+ "",
61
+ "--concurrency",
62
+ action="store",
63
+ metavar="LIBS",
64
+ help=oneline(
65
+ """
66
+ Properly measure code using a concurrency library.
67
+ Valid values are: {}, or a comma-list of them.
68
+ """
54
69
  ).format(", ".join(sorted(CoverageConfig.CONCURRENCY_CHOICES))),
55
70
  )
56
71
  context = optparse.make_option(
57
- "", "--context", action="store", metavar="LABEL",
72
+ "",
73
+ "--context",
74
+ action="store",
75
+ metavar="LABEL",
58
76
  help="The context label to record for this coverage run.",
59
77
  )
60
78
  contexts = optparse.make_option(
61
- "", "--contexts", action="store", metavar="REGEX1,REGEX2,...",
62
- help=(
63
- "Only display data from lines covered in the given contexts. " +
64
- "Accepts Python regexes, which must be quoted."
79
+ "",
80
+ "--contexts",
81
+ action="store",
82
+ metavar="REGEX1,REGEX2,...",
83
+ help=oneline(
84
+ """
85
+ Only display data from lines covered in the given contexts.
86
+ Accepts Python regexes, which must be quoted.
87
+ """
65
88
  ),
66
89
  )
67
90
  datafile = optparse.make_option(
68
- "", "--data-file", action="store", metavar="DATAFILE",
69
- help=(
70
- "Base name of the data files to operate on. " +
71
- "Defaults to '.coverage'. [env: COVERAGE_FILE]"
91
+ "",
92
+ "--data-file",
93
+ action="store",
94
+ metavar="DATAFILE",
95
+ help=oneline(
96
+ """
97
+ Base name of the data files to operate on.
98
+ Defaults to '.coverage'. [env: COVERAGE_FILE]
99
+ """
72
100
  ),
73
101
  )
74
102
  datafle_input = optparse.make_option(
75
- "", "--data-file", action="store", metavar="INFILE",
76
- help=(
77
- "Read coverage data for report generation from this file. " +
78
- "Defaults to '.coverage'. [env: COVERAGE_FILE]"
103
+ "",
104
+ "--data-file",
105
+ action="store",
106
+ metavar="INFILE",
107
+ help=oneline(
108
+ """
109
+ Read coverage data for report generation from this file.
110
+ Defaults to '.coverage'. [env: COVERAGE_FILE]
111
+ """
79
112
  ),
80
113
  )
81
114
  datafile_output = optparse.make_option(
82
- "", "--data-file", action="store", metavar="OUTFILE",
83
- help=(
84
- "Write the recorded coverage data to this file. " +
85
- "Defaults to '.coverage'. [env: COVERAGE_FILE]"
115
+ "",
116
+ "--data-file",
117
+ action="store",
118
+ metavar="OUTFILE",
119
+ help=oneline(
120
+ """
121
+ Write the recorded coverage data to this file.
122
+ Defaults to '.coverage'. [env: COVERAGE_FILE]
123
+ """
86
124
  ),
87
125
  )
88
126
  debug = optparse.make_option(
89
- "", "--debug", action="store", metavar="OPTS",
127
+ "",
128
+ "--debug",
129
+ action="store",
130
+ metavar="OPTS",
90
131
  help="Debug options, separated by commas. [env: COVERAGE_DEBUG]",
91
132
  )
92
133
  directory = optparse.make_option(
93
- "-d", "--directory", action="store", metavar="DIR",
134
+ "-d",
135
+ "--directory",
136
+ action="store",
137
+ metavar="DIR",
94
138
  help="Write the output files to DIR.",
95
139
  )
96
140
  fail_under = optparse.make_option(
97
- "", "--fail-under", action="store", metavar="MIN", type="float",
141
+ "",
142
+ "--fail-under",
143
+ action="store",
144
+ metavar="MIN",
145
+ type="float",
98
146
  help="Exit with a status of 2 if the total coverage is less than MIN.",
99
147
  )
100
148
  format = optparse.make_option(
101
- "", "--format", action="store", metavar="FORMAT",
149
+ "",
150
+ "--format",
151
+ action="store",
152
+ metavar="FORMAT",
102
153
  help="Output format, either text (default), markdown, or total.",
103
154
  )
104
155
  help = optparse.make_option(
105
- "-h", "--help", action="store_true",
156
+ "-h",
157
+ "--help",
158
+ action="store_true",
106
159
  help="Get help on this command.",
107
160
  )
108
161
  ignore_errors = optparse.make_option(
109
- "-i", "--ignore-errors", action="store_true",
162
+ "-i",
163
+ "--ignore-errors",
164
+ action="store_true",
110
165
  help="Ignore errors while reading source files.",
111
166
  )
112
167
  include = optparse.make_option(
113
- "", "--include", action="store", metavar="PAT1,PAT2,...",
114
- help=(
115
- "Include only files whose paths match one of these patterns. " +
116
- "Accepts shell-style wildcards, which must be quoted."
168
+ "",
169
+ "--include",
170
+ action="store",
171
+ metavar="PAT1,PAT2,...",
172
+ help=oneline(
173
+ """
174
+ Include only files whose paths match one of these patterns.
175
+ Accepts shell-style wildcards, which must be quoted.
176
+ """
117
177
  ),
118
178
  )
119
179
  keep = optparse.make_option(
120
- "", "--keep", action="store_true",
180
+ "",
181
+ "--keep",
182
+ action="store_true",
121
183
  help="Keep original coverage files, otherwise they are deleted.",
122
184
  )
123
185
  pylib = optparse.make_option(
124
- "-L", "--pylib", action="store_true",
125
- help=(
126
- "Measure coverage even inside the Python installed library, " +
127
- "which isn't done by default."
186
+ "-L",
187
+ "--pylib",
188
+ action="store_true",
189
+ help=oneline(
190
+ """
191
+ Measure coverage even inside the Python installed library,
192
+ which isn't done by default.
193
+ """
128
194
  ),
129
195
  )
130
196
  show_missing = optparse.make_option(
131
- "-m", "--show-missing", action="store_true",
197
+ "-m",
198
+ "--show-missing",
199
+ action="store_true",
132
200
  help="Show line numbers of statements in each module that weren't executed.",
133
201
  )
134
202
  module = optparse.make_option(
135
- "-m", "--module", action="store_true",
136
- help=(
137
- "<pyfile> is an importable Python module, not a script path, " +
138
- "to be run as 'python -m' would run it."
203
+ "-m",
204
+ "--module",
205
+ action="store_true",
206
+ help=oneline(
207
+ """
208
+ <pyfile> is an importable Python module, not a script path,
209
+ to be run as 'python -m' would run it.
210
+ """
139
211
  ),
140
212
  )
141
213
  omit = optparse.make_option(
142
- "", "--omit", action="store", metavar="PAT1,PAT2,...",
143
- help=(
144
- "Omit files whose paths match one of these patterns. " +
145
- "Accepts shell-style wildcards, which must be quoted."
214
+ "",
215
+ "--omit",
216
+ action="store",
217
+ metavar="PAT1,PAT2,...",
218
+ help=oneline(
219
+ """
220
+ Omit files whose paths match one of these patterns.
221
+ Accepts shell-style wildcards, which must be quoted.
222
+ """
146
223
  ),
147
224
  )
148
225
  output_xml = optparse.make_option(
149
- "-o", "", action="store", dest="outfile", metavar="OUTFILE",
226
+ "-o",
227
+ "",
228
+ action="store",
229
+ dest="outfile",
230
+ metavar="OUTFILE",
150
231
  help="Write the XML report to this file. Defaults to 'coverage.xml'",
151
232
  )
152
233
  output_json = optparse.make_option(
153
- "-o", "", action="store", dest="outfile", metavar="OUTFILE",
234
+ "-o",
235
+ "",
236
+ action="store",
237
+ dest="outfile",
238
+ metavar="OUTFILE",
154
239
  help="Write the JSON report to this file. Defaults to 'coverage.json'",
155
240
  )
156
241
  output_lcov = optparse.make_option(
157
- "-o", "", action="store", dest="outfile", metavar="OUTFILE",
242
+ "-o",
243
+ "",
244
+ action="store",
245
+ dest="outfile",
246
+ metavar="OUTFILE",
158
247
  help="Write the LCOV report to this file. Defaults to 'coverage.lcov'",
159
248
  )
160
249
  json_pretty_print = optparse.make_option(
161
- "", "--pretty-print", action="store_true",
250
+ "",
251
+ "--pretty-print",
252
+ action="store_true",
162
253
  help="Format the JSON for human readers.",
163
254
  )
164
255
  parallel_mode = optparse.make_option(
165
- "-p", "--parallel-mode", action="store_true",
166
- help=(
167
- "Append the machine name, process id and random number to the " +
168
- "data file name to simplify collecting data from " +
169
- "many processes."
256
+ "-p",
257
+ "--parallel-mode",
258
+ action="store_true",
259
+ help=oneline(
260
+ """
261
+ Append a unique suffix to the data file name to collect separate
262
+ data from multiple processes.
263
+ """
170
264
  ),
171
265
  )
172
266
  precision = optparse.make_option(
173
- "", "--precision", action="store", metavar="N", type=int,
174
- help=(
175
- "Number of digits after the decimal point to display for " +
176
- "reported coverage percentages."
267
+ "",
268
+ "--precision",
269
+ action="store",
270
+ metavar="N",
271
+ type=int,
272
+ help=oneline(
273
+ """
274
+ Number of digits after the decimal point to display for
275
+ reported coverage percentages.
276
+ """
177
277
  ),
178
278
  )
179
279
  quiet = optparse.make_option(
180
- "-q", "--quiet", action="store_true",
280
+ "-q",
281
+ "--quiet",
282
+ action="store_true",
181
283
  help="Don't print messages about what is happening.",
182
284
  )
183
285
  rcfile = optparse.make_option(
184
- "", "--rcfile", action="store",
185
- help=(
186
- "Specify configuration file. " +
187
- "By default '.coveragerc', 'setup.cfg', 'tox.ini', and " +
188
- "'pyproject.toml' are tried. [env: COVERAGE_RCFILE]"
286
+ "",
287
+ "--rcfile",
288
+ action="store",
289
+ help=oneline(
290
+ """
291
+ Specify configuration file.
292
+ By default '.coveragerc', 'setup.cfg', 'tox.ini', and
293
+ 'pyproject.toml' are tried. [env: COVERAGE_RCFILE]
294
+ """
295
+ ),
296
+ )
297
+ save_signal = optparse.make_option(
298
+ "",
299
+ "--save-signal",
300
+ action="store",
301
+ metavar="SIGNAL",
302
+ choices=["USR1", "USR2"],
303
+ help=oneline(
304
+ """
305
+ Specify a signal that will trigger coverage to write its collected data.
306
+ Supported values are: USR1, USR2. Not available on Windows.
307
+ """
189
308
  ),
190
309
  )
191
310
  show_contexts = optparse.make_option(
192
- "--show-contexts", action="store_true",
311
+ "--show-contexts",
312
+ action="store_true",
193
313
  help="Show contexts for covered lines.",
194
314
  )
195
315
  skip_covered = optparse.make_option(
196
- "--skip-covered", action="store_true",
316
+ "--skip-covered",
317
+ action="store_true",
197
318
  help="Skip files with 100% coverage.",
198
319
  )
199
320
  no_skip_covered = optparse.make_option(
200
- "--no-skip-covered", action="store_false", dest="skip_covered",
321
+ "--no-skip-covered",
322
+ action="store_false",
323
+ dest="skip_covered",
201
324
  help="Disable --skip-covered.",
202
325
  )
203
326
  skip_empty = optparse.make_option(
204
- "--skip-empty", action="store_true",
327
+ "--skip-empty",
328
+ action="store_true",
205
329
  help="Skip files with no code.",
206
330
  )
207
331
  sort = optparse.make_option(
208
- "--sort", action="store", metavar="COLUMN",
209
- help=(
210
- "Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. " +
211
- "Default is name."
332
+ "--sort",
333
+ action="store",
334
+ metavar="COLUMN",
335
+ help=oneline(
336
+ """
337
+ Sort the report by the named column: name, stmts, miss, branch, brpart, or cover.
338
+ Default is name.
339
+ """
212
340
  ),
213
341
  )
214
342
  source = optparse.make_option(
215
- "", "--source", action="store", metavar="SRC1,SRC2,...",
343
+ "",
344
+ "--source",
345
+ action="store",
346
+ metavar="SRC1,SRC2,...",
216
347
  help="A list of directories or importable names of code to measure.",
217
348
  )
218
349
  timid = optparse.make_option(
219
- "", "--timid", action="store_true",
350
+ "",
351
+ "--timid",
352
+ action="store_true",
220
353
  help="Use the slower Python trace function core.",
221
354
  )
222
355
  title = optparse.make_option(
223
- "", "--title", action="store", metavar="TITLE",
356
+ "",
357
+ "--title",
358
+ action="store",
359
+ metavar="TITLE",
224
360
  help="A text string to use as the title on the HTML.",
225
361
  )
226
362
  version = optparse.make_option(
227
- "", "--version", action="store_true",
363
+ "",
364
+ "--version",
365
+ action="store_true",
228
366
  help="Display version information and exit.",
229
367
  )
230
368
 
@@ -264,6 +402,7 @@ class CoverageOptionParser(optparse.OptionParser):
264
402
  pylib=None,
265
403
  quiet=None,
266
404
  rcfile=True,
405
+ save_signal=None,
267
406
  show_contexts=None,
268
407
  show_missing=None,
269
408
  skip_covered=None,
@@ -279,6 +418,7 @@ class CoverageOptionParser(optparse.OptionParser):
279
418
 
280
419
  class OptionParserError(Exception):
281
420
  """Used to stop the optparse error handler ending the process."""
421
+
282
422
  pass
283
423
 
284
424
  def parse_args_ok(self, args: list[str]) -> tuple[bool, optparse.Values | None, list[str]]:
@@ -305,10 +445,12 @@ class GlobalOptionParser(CoverageOptionParser):
305
445
  def __init__(self) -> None:
306
446
  super().__init__()
307
447
 
308
- self.add_options([
309
- Opts.help,
310
- Opts.version,
311
- ])
448
+ self.add_options(
449
+ [
450
+ Opts.help,
451
+ Opts.version,
452
+ ]
453
+ )
312
454
 
313
455
 
314
456
  class CmdOptionParser(CoverageOptionParser):
@@ -339,12 +481,12 @@ class CmdOptionParser(CoverageOptionParser):
339
481
  self.add_options(options)
340
482
  self.cmd = action
341
483
 
342
- def __eq__(self, other: str) -> bool: # type: ignore[override]
484
+ def __eq__(self, other: str) -> bool: # type: ignore[override]
343
485
  # A convenience equality, so that I can put strings in unit test
344
486
  # results, and they will compare equal to objects.
345
- return (other == f"<CmdOptionParser:{self.cmd}>")
487
+ return other == f"<CmdOptionParser:{self.cmd}>"
346
488
 
347
- __hash__ = None # type: ignore[assignment]
489
+ __hash__ = None # type: ignore[assignment]
348
490
 
349
491
  def get_prog_name(self) -> str:
350
492
  """Override of an undocumented function in optparse.OptionParser."""
@@ -353,6 +495,7 @@ class CmdOptionParser(CoverageOptionParser):
353
495
  # Include the sub-command for this parser as part of the command.
354
496
  return f"{program_name} {self.cmd}"
355
497
 
498
+
356
499
  # In lists of Opts, keep them alphabetized by the option names as they appear
357
500
  # on the command line, since these lists determine the order of the options in
358
501
  # the help output.
@@ -374,14 +517,16 @@ COMMANDS = {
374
517
  Opts.ignore_errors,
375
518
  Opts.include,
376
519
  Opts.omit,
377
- ] + GLOBAL_ARGS,
520
+ ]
521
+ + GLOBAL_ARGS,
378
522
  usage="[options] [modules]",
379
- description=(
380
- "Make annotated copies of the given files, marking statements that are executed " +
381
- "with > and statements that are missed with !."
523
+ description=oneline(
524
+ """
525
+ Make annotated copies of the given files, marking statements that are executed
526
+ with > and statements that are missed with !.
527
+ """
382
528
  ),
383
529
  ),
384
-
385
530
  "combine": CmdOptionParser(
386
531
  "combine",
387
532
  [
@@ -389,47 +534,52 @@ COMMANDS = {
389
534
  Opts.datafile,
390
535
  Opts.keep,
391
536
  Opts.quiet,
392
- ] + GLOBAL_ARGS,
537
+ ]
538
+ + GLOBAL_ARGS,
393
539
  usage="[options] <path1> <path2> ... <pathN>",
394
- description=(
395
- "Combine data from multiple coverage files. " +
396
- "The combined results are written to a single " +
397
- "file representing the union of the data. The positional " +
398
- "arguments are data files or directories containing data files. " +
399
- "If no paths are provided, data files in the default data file's " +
400
- "directory are combined."
540
+ description=oneline(
541
+ """
542
+ Combine data from multiple coverage files.
543
+ The combined results are written to a single
544
+ file representing the union of the data. The positional
545
+ arguments are data files or directories containing data files.
546
+ If no paths are provided, data files in the default data file's
547
+ directory are combined.
548
+ """
401
549
  ),
402
550
  ),
403
-
404
551
  "debug": CmdOptionParser(
405
- "debug", GLOBAL_ARGS,
552
+ "debug",
553
+ GLOBAL_ARGS,
406
554
  usage="<topic>",
407
- description=(
408
- "Display information about the internals of coverage.py, " +
409
- "for diagnosing problems. " +
410
- "Topics are: " +
411
- "'data' to show a summary of the collected data; " +
412
- "'sys' to show installation information; " +
413
- "'config' to show the configuration; " +
414
- "'premain' to show what is calling coverage; " +
415
- "'pybehave' to show internal flags describing Python behavior."
555
+ description=oneline(
556
+ """
557
+ Display information about the internals of coverage.py,
558
+ for diagnosing problems.
559
+ Topics are:
560
+ 'data' to show a summary of the collected data;
561
+ 'sys' to show installation information;
562
+ 'config' to show the configuration;
563
+ 'premain' to show what is calling coverage;
564
+ 'pybehave' to show internal flags describing Python behavior;
565
+ 'sqlite' to show SQLite compilation options.
566
+ """
416
567
  ),
417
568
  ),
418
-
419
569
  "erase": CmdOptionParser(
420
570
  "erase",
421
571
  [
422
572
  Opts.datafile,
423
- ] + GLOBAL_ARGS,
573
+ ]
574
+ + GLOBAL_ARGS,
424
575
  description="Erase previously collected coverage data.",
425
576
  ),
426
-
427
577
  "help": CmdOptionParser(
428
- "help", GLOBAL_ARGS,
578
+ "help",
579
+ GLOBAL_ARGS,
429
580
  usage="[command]",
430
581
  description="Describe how to use coverage.py",
431
582
  ),
432
-
433
583
  "html": CmdOptionParser(
434
584
  "html",
435
585
  [
@@ -447,15 +597,17 @@ COMMANDS = {
447
597
  Opts.no_skip_covered,
448
598
  Opts.skip_empty,
449
599
  Opts.title,
450
- ] + GLOBAL_ARGS,
600
+ ]
601
+ + GLOBAL_ARGS,
451
602
  usage="[options] [modules]",
452
- description=(
453
- "Create an HTML report of the coverage of the files. " +
454
- "Each file gets its own page, with the source decorated to show " +
455
- "executed, excluded, and missed lines."
603
+ description=oneline(
604
+ """
605
+ Create an HTML report of the coverage of the files.
606
+ Each file gets its own page, with the source decorated to show
607
+ executed, excluded, and missed lines.
608
+ """
456
609
  ),
457
610
  ),
458
-
459
611
  "json": CmdOptionParser(
460
612
  "json",
461
613
  [
@@ -469,11 +621,11 @@ COMMANDS = {
469
621
  Opts.json_pretty_print,
470
622
  Opts.quiet,
471
623
  Opts.show_contexts,
472
- ] + GLOBAL_ARGS,
624
+ ]
625
+ + GLOBAL_ARGS,
473
626
  usage="[options] [modules]",
474
627
  description="Generate a JSON report of coverage results.",
475
628
  ),
476
-
477
629
  "lcov": CmdOptionParser(
478
630
  "lcov",
479
631
  [
@@ -484,11 +636,11 @@ COMMANDS = {
484
636
  Opts.output_lcov,
485
637
  Opts.omit,
486
638
  Opts.quiet,
487
- ] + GLOBAL_ARGS,
639
+ ]
640
+ + GLOBAL_ARGS,
488
641
  usage="[options] [modules]",
489
642
  description="Generate an LCOV report of coverage results.",
490
643
  ),
491
-
492
644
  "report": CmdOptionParser(
493
645
  "report",
494
646
  [
@@ -505,11 +657,11 @@ COMMANDS = {
505
657
  Opts.skip_covered,
506
658
  Opts.no_skip_covered,
507
659
  Opts.skip_empty,
508
- ] + GLOBAL_ARGS,
660
+ ]
661
+ + GLOBAL_ARGS,
509
662
  usage="[options] [modules]",
510
663
  description="Report coverage statistics on modules.",
511
664
  ),
512
-
513
665
  "run": CmdOptionParser(
514
666
  "run",
515
667
  [
@@ -523,13 +675,14 @@ COMMANDS = {
523
675
  Opts.omit,
524
676
  Opts.pylib,
525
677
  Opts.parallel_mode,
678
+ Opts.save_signal,
526
679
  Opts.source,
527
680
  Opts.timid,
528
- ] + GLOBAL_ARGS,
681
+ ]
682
+ + GLOBAL_ARGS,
529
683
  usage="[options] <pyfile> [program options]",
530
684
  description="Run a Python program, measuring code execution.",
531
685
  ),
532
-
533
686
  "xml": CmdOptionParser(
534
687
  "xml",
535
688
  [
@@ -541,7 +694,8 @@ COMMANDS = {
541
694
  Opts.output_xml,
542
695
  Opts.quiet,
543
696
  Opts.skip_empty,
544
- ] + GLOBAL_ARGS,
697
+ ]
698
+ + GLOBAL_ARGS,
545
699
  usage="[options] [modules]",
546
700
  description="Generate an XML report of coverage results.",
547
701
  ),
@@ -569,12 +723,12 @@ def show_help(
569
723
  # get back to the original form.
570
724
  auto_suffix = "-script.py"
571
725
  if program_name.endswith(auto_suffix):
572
- program_name = program_name[:-len(auto_suffix)]
726
+ program_name = program_name[: -len(auto_suffix)]
573
727
 
574
728
  help_params = dict(coverage.__dict__)
575
729
  help_params["__url__"] = __url__
576
730
  help_params["program_name"] = program_name
577
- if HAS_CTRACER:
731
+ if CTRACER_FILE:
578
732
  help_params["extension_modifier"] = "with C extension"
579
733
  else:
580
734
  help_params["extension_modifier"] = "without C extension"
@@ -807,6 +961,11 @@ class CoverageScript:
807
961
 
808
962
  return False
809
963
 
964
+ def do_signal_save(self, _signum: int, _frame: types.FrameType | None) -> None:
965
+ """Signal handler to save coverage report"""
966
+ print("Saving coverage data...", flush=True)
967
+ self.coverage.save()
968
+
810
969
  def do_run(self, options: optparse.Values, args: list[str]) -> int:
811
970
  """Implementation of 'coverage run'."""
812
971
 
@@ -837,9 +996,9 @@ class CoverageScript:
837
996
  # they will be None if they have not been specified.
838
997
  if getattr(options, opt_name) is not None:
839
998
  show_help(
840
- "Options affecting multiprocessing must only be specified " +
841
- "in a configuration file.\n" +
842
- f"Remove --{opt_name} from the command line.",
999
+ "Options affecting multiprocessing must only be specified "
1000
+ + "in a configuration file.\n"
1001
+ + f"Remove --{opt_name} from the command line.",
843
1002
  )
844
1003
  return ERR
845
1004
 
@@ -851,6 +1010,13 @@ class CoverageScript:
851
1010
  if options.append:
852
1011
  self.coverage.load()
853
1012
 
1013
+ if options.save_signal:
1014
+ if env.WINDOWS:
1015
+ show_help("--save-signal is not supported on Windows.")
1016
+ return ERR
1017
+ sig = getattr(signal, f"SIG{options.save_signal}")
1018
+ signal.signal(sig, self.do_signal_save)
1019
+
854
1020
  # Run the script.
855
1021
  self.coverage.start()
856
1022
  code_ran = True
@@ -870,7 +1036,10 @@ class CoverageScript:
870
1036
  """Implementation of 'coverage debug'."""
871
1037
 
872
1038
  if not args:
873
- show_help("What information would you like: config, data, sys, premain, pybehave?")
1039
+ show_help(
1040
+ "What information would you like: "
1041
+ + "config, data, sys, premain, pybehave, sqlite?"
1042
+ )
874
1043
  return ERR
875
1044
  if args[1:]:
876
1045
  show_help("Only one topic at a time, please")
@@ -892,6 +1061,8 @@ class CoverageScript:
892
1061
  print(short_stack(full=True))
893
1062
  elif args[0] == "pybehave":
894
1063
  write_formatted_info(print, "pybehave", env.debug_info())
1064
+ elif args[0] == "sqlite":
1065
+ write_formatted_info(print, "sqlite", CoverageData.sys_info())
895
1066
  else:
896
1067
  show_help(f"Don't know what you mean by {args[0]!r}")
897
1068
  return ERR
@@ -948,12 +1119,12 @@ HELP_TOPICS = {
948
1119
 
949
1120
  Use "{program_name} help <command>" for detailed help on any command.
950
1121
  """,
951
-
952
- "minimum_help": (
953
- "Code coverage for Python, version {__version__} {extension_modifier}. " +
954
- "Use '{program_name} help' for help."
1122
+ "minimum_help": oneline(
1123
+ """
1124
+ Code coverage for Python, version {__version__} {extension_modifier}.
1125
+ Use '{program_name} help' for help.
1126
+ """
955
1127
  ),
956
-
957
1128
  "version": "Coverage.py, version {__version__} {extension_modifier}",
958
1129
  }
959
1130
 
@@ -972,11 +1143,13 @@ def main(argv: list[str] | None = None) -> int | None:
972
1143
  # An exception was caught while running the product code. The
973
1144
  # sys.exc_info() return tuple is packed into an _ExceptionDuringRun
974
1145
  # exception.
975
- traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter
1146
+ traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter
976
1147
  status = ERR
977
- except _BaseCoverageException as err:
1148
+ except CoverageException as err:
978
1149
  # A controlled error inside coverage.py: print the message to the user.
979
1150
  msg = err.args[0]
1151
+ if err.slug:
1152
+ msg = f"{msg.rstrip('.')}; see {__url__}/messages.html#error-{err.slug}"
980
1153
  print(msg)
981
1154
  status = ERR
982
1155
  except SystemExit as err:
@@ -987,16 +1160,18 @@ def main(argv: list[str] | None = None) -> int | None:
987
1160
  status = None
988
1161
  return status
989
1162
 
1163
+
990
1164
  # Profiling using ox_profile. Install it from GitHub:
991
1165
  # pip install git+https://github.com/emin63/ox_profile.git
992
1166
  #
993
1167
  # $set_env.py: COVERAGE_PROFILE - Set to use ox_profile.
994
1168
  _profile = os.getenv("COVERAGE_PROFILE")
995
- if _profile: # pragma: debugging
996
- from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error
1169
+ if _profile: # pragma: debugging
1170
+ from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error
1171
+
997
1172
  original_main = main
998
1173
 
999
- def main( # pylint: disable=function-redefined
1174
+ def main( # pylint: disable=function-redefined
1000
1175
  argv: list[str] | None = None,
1001
1176
  ) -> int | None:
1002
1177
  """A wrapper around main that profiles."""