Coverage for coverage / cmdline.py: 98.272%
333 statements
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
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
4"""Command-line support for coverage.py."""
6from __future__ import annotations 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
8import glob 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
9import optparse 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
10import os 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
11import os.path 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
12import shlex 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
13import signal 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
14import sys 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
15import textwrap 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
16import traceback 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
17import types 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
18from typing import Any, NoReturn, cast 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
20import coverage 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
21from coverage import Coverage, env 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
22from coverage.config import CoverageConfig 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
23from coverage.control import DEFAULT_DATAFILE 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
24from coverage.core import CTRACER_FILE 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
25from coverage.data import CoverageData, combinable_files, debug_data_file 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
26from coverage.debug import info_header, short_stack, write_formatted_info 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
27from coverage.exceptions import NoSource, CoverageException, _ExceptionDuringRun 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
28from coverage.execfile import PyRunner 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
29from coverage.results import display_covered, should_fail_under 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
30from coverage.version import __url__ 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
32# When adding to this file, alphabetization is important. Look for
33# "alphabetize" comments throughout.
36def oneline(text: str) -> str: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
37 """Turn a multi-line string into one line for help to reformat nicely."""
38 return " ".join(text.split()) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
41class Opts: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
42 """A namespace class for individual options we'll build parsers from."""
44 # Keep these entries alphabetized (roughly) by the option name as it
45 # appears on the command line.
47 append = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
48 "-a",
49 "--append",
50 action="store_true",
51 help="Append data to the data file. Otherwise it starts clean each time.",
52 )
53 branch = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
54 "",
55 "--branch",
56 action="store_true",
57 help="Measure branch coverage in addition to statement coverage.",
58 )
59 concurrency = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
69 ).format(", ".join(sorted(CoverageConfig.CONCURRENCY_CHOICES))),
70 )
71 context = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
72 "",
73 "--context",
74 action="store",
75 metavar="LABEL",
76 help="The context label to record for this coverage run.",
77 )
78 contexts = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
88 ),
89 )
90 datafile = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
100 ),
101 )
102 datafle_input = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
112 ),
113 )
114 datafile_output = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
124 ),
125 )
126 debug = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
127 "",
128 "--debug",
129 action="store",
130 metavar="OPTS",
131 help="Debug options, separated by commas. [env: COVERAGE_DEBUG]",
132 )
133 directory = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
134 "-d",
135 "--directory",
136 action="store",
137 metavar="DIR",
138 help="Write the output files to DIR.",
139 )
140 fail_under = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
141 "",
142 "--fail-under",
143 action="store",
144 metavar="MIN",
145 type="float",
146 help="Exit with a status of 2 if the total coverage is less than MIN.",
147 )
148 format = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
149 "",
150 "--format",
151 action="store",
152 metavar="FORMAT",
153 help="Output format, either text (default), markdown, or total.",
154 )
155 help = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
156 "-h",
157 "--help",
158 action="store_true",
159 help="Get help on this command.",
160 )
161 ignore_errors = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
162 "-i",
163 "--ignore-errors",
164 action="store_true",
165 help="Ignore errors while reading source files.",
166 )
167 include = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
177 ),
178 )
179 keep = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
180 "",
181 "--keep",
182 action="store_true",
183 help="Keep original coverage files, otherwise they are deleted.",
184 )
185 pylib = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
194 ),
195 )
196 show_missing = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
197 "-m",
198 "--show-missing",
199 action="store_true",
200 help="Show line numbers of statements in each module that weren't executed.",
201 )
202 module = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
211 ),
212 )
213 omit = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
223 ),
224 )
225 output_xml = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
226 "-o",
227 "",
228 action="store",
229 dest="outfile",
230 metavar="OUTFILE",
231 help="Write the XML report to this file. Defaults to 'coverage.xml'",
232 )
233 output_json = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
234 "-o",
235 "",
236 action="store",
237 dest="outfile",
238 metavar="OUTFILE",
239 help="Write the JSON report to this file. Defaults to 'coverage.json'",
240 )
241 output_lcov = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
242 "-o",
243 "",
244 action="store",
245 dest="outfile",
246 metavar="OUTFILE",
247 help="Write the LCOV report to this file. Defaults to 'coverage.lcov'",
248 )
249 json_pretty_print = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
250 "",
251 "--pretty-print",
252 action="store_true",
253 help="Format the JSON for human readers.",
254 )
255 parallel_mode = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
264 ),
265 )
266 precision = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
277 ),
278 )
279 quiet = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
280 "-q",
281 "--quiet",
282 action="store_true",
283 help="Don't print messages about what is happening.",
284 )
285 rcfile = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
308 ),
309 )
310 show_contexts = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
311 "--show-contexts",
312 action="store_true",
313 help="Show contexts for covered lines.",
314 )
315 skip_covered = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
316 "--skip-covered",
317 action="store_true",
318 help="Skip files with 100% coverage.",
319 )
320 no_skip_covered = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
321 "--no-skip-covered",
322 action="store_false",
323 dest="skip_covered",
324 help="Disable --skip-covered.",
325 )
326 skip_empty = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
327 "--skip-empty",
328 action="store_true",
329 help="Skip files with no code.",
330 )
331 sort = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
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 """
340 ),
341 )
342 source = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
343 "",
344 "--source",
345 action="store",
346 metavar="SRC1,SRC2,...",
347 help="A list of directories or importable names of code to measure.",
348 )
349 timid = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
350 "",
351 "--timid",
352 action="store_true",
353 help="Use the slower Python trace function core.",
354 )
355 title = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
356 "",
357 "--title",
358 action="store",
359 metavar="TITLE",
360 help="A text string to use as the title on the HTML.",
361 )
362 version = optparse.make_option( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
363 "",
364 "--version",
365 action="store_true",
366 help="Display version information and exit.",
367 )
370class CoverageOptionParser(optparse.OptionParser): 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
371 """Base OptionParser for coverage.py.
373 Problems don't exit the program.
374 Defaults are initialized for all options.
376 """
378 def __init__(self, *args: Any, **kwargs: Any) -> None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
379 kwargs["add_help_option"] = False 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
380 super().__init__(*args, **kwargs) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
381 self.set_defaults( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
382 # Keep these arguments alphabetized by their names.
383 action=None,
384 append=None,
385 branch=None,
386 concurrency=None,
387 context=None,
388 contexts=None,
389 data_file=None,
390 debug=None,
391 directory=None,
392 fail_under=None,
393 format=None,
394 help=None,
395 ignore_errors=None,
396 include=None,
397 keep=None,
398 module=None,
399 omit=None,
400 parallel_mode=None,
401 precision=None,
402 pylib=None,
403 quiet=None,
404 rcfile=True,
405 save_signal=None,
406 show_contexts=None,
407 show_missing=None,
408 skip_covered=None,
409 skip_empty=None,
410 sort=None,
411 source=None,
412 timid=None,
413 title=None,
414 version=None,
415 )
417 self.disable_interspersed_args() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
419 class OptionParserError(Exception): 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
420 """Used to stop the optparse error handler ending the process."""
422 pass 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
424 def parse_args_ok(self, args: list[str]) -> tuple[bool, optparse.Values | None, list[str]]: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
425 """Call optparse.parse_args, but return a triple:
427 (ok, options, args)
429 """
430 try: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
431 options, args = super().parse_args(args) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
432 except self.OptionParserError: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
433 return False, None, [] 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
434 return True, options, args 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
436 def error(self, msg: str) -> NoReturn: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
437 """Override optparse.error so sys.exit doesn't get called."""
438 show_help(msg) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
439 raise self.OptionParserError 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
442class GlobalOptionParser(CoverageOptionParser): 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
443 """Command-line parser for coverage.py global option arguments."""
445 def __init__(self) -> None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
446 super().__init__() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
448 self.add_options( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
449 [
450 Opts.help,
451 Opts.version,
452 ]
453 )
456class CmdOptionParser(CoverageOptionParser): 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
457 """Parse one of the new-style commands for coverage.py."""
459 def __init__( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw
460 self,
461 action: str,
462 options: list[optparse.Option],
463 description: str,
464 usage: str | None = None,
465 ):
466 """Create an OptionParser for a coverage.py command.
468 `action` is the slug to put into `options.action`.
469 `options` is a list of Option's for the command.
470 `description` is the description of the command, for the help text.
471 `usage` is the usage string to display in help.
473 """
474 if usage: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
475 usage = "%prog " + usage 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
476 super().__init__( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
477 usage=usage,
478 description=description,
479 )
480 self.set_defaults(action=action) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
481 self.add_options(options) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
482 self.cmd = action 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
484 def __eq__(self, other: str) -> bool: # type: ignore[override] 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
485 # A convenience equality, so that I can put strings in unit test
486 # results, and they will compare equal to objects.
487 return other == f"<CmdOptionParser:{self.cmd}>" 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
489 __hash__ = None # type: ignore[assignment] 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
491 def get_prog_name(self) -> str: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
492 """Override of an undocumented function in optparse.OptionParser."""
493 program_name = super().get_prog_name() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
495 # Include the sub-command for this parser as part of the command.
496 return f"{program_name} {self.cmd}" 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
499# In lists of Opts, keep them alphabetized by the option names as they appear
500# on the command line, since these lists determine the order of the options in
501# the help output.
502#
503# In COMMANDS, keep the keys (command names) alphabetized.
505GLOBAL_ARGS = [ 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
506 Opts.debug,
507 Opts.help,
508 Opts.rcfile,
509]
511COMMANDS = { 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
512 "annotate": CmdOptionParser(
513 "annotate",
514 [
515 Opts.directory,
516 Opts.datafle_input,
517 Opts.ignore_errors,
518 Opts.include,
519 Opts.omit,
520 ]
521 + GLOBAL_ARGS,
522 usage="[options] [modules]",
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 """
528 ),
529 ),
530 "combine": CmdOptionParser(
531 "combine",
532 [
533 Opts.append,
534 Opts.datafile,
535 Opts.keep,
536 Opts.quiet,
537 ]
538 + GLOBAL_ARGS,
539 usage="[options] <path1> <path2> ... <pathN>",
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 """
549 ),
550 ),
551 "debug": CmdOptionParser(
552 "debug",
553 GLOBAL_ARGS,
554 usage="<topic>",
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 """
567 ),
568 ),
569 "erase": CmdOptionParser(
570 "erase",
571 [
572 Opts.datafile,
573 ]
574 + GLOBAL_ARGS,
575 description="Erase previously collected coverage data.",
576 ),
577 "help": CmdOptionParser(
578 "help",
579 GLOBAL_ARGS,
580 usage="[command]",
581 description="Describe how to use coverage.py",
582 ),
583 "html": CmdOptionParser(
584 "html",
585 [
586 Opts.contexts,
587 Opts.directory,
588 Opts.datafle_input,
589 Opts.fail_under,
590 Opts.ignore_errors,
591 Opts.include,
592 Opts.omit,
593 Opts.precision,
594 Opts.quiet,
595 Opts.show_contexts,
596 Opts.skip_covered,
597 Opts.no_skip_covered,
598 Opts.skip_empty,
599 Opts.title,
600 ]
601 + GLOBAL_ARGS,
602 usage="[options] [modules]",
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 """
609 ),
610 ),
611 "json": CmdOptionParser(
612 "json",
613 [
614 Opts.contexts,
615 Opts.datafle_input,
616 Opts.fail_under,
617 Opts.ignore_errors,
618 Opts.include,
619 Opts.omit,
620 Opts.output_json,
621 Opts.json_pretty_print,
622 Opts.quiet,
623 Opts.show_contexts,
624 ]
625 + GLOBAL_ARGS,
626 usage="[options] [modules]",
627 description="Generate a JSON report of coverage results.",
628 ),
629 "lcov": CmdOptionParser(
630 "lcov",
631 [
632 Opts.datafle_input,
633 Opts.fail_under,
634 Opts.ignore_errors,
635 Opts.include,
636 Opts.output_lcov,
637 Opts.omit,
638 Opts.quiet,
639 ]
640 + GLOBAL_ARGS,
641 usage="[options] [modules]",
642 description="Generate an LCOV report of coverage results.",
643 ),
644 "report": CmdOptionParser(
645 "report",
646 [
647 Opts.contexts,
648 Opts.datafle_input,
649 Opts.fail_under,
650 Opts.format,
651 Opts.ignore_errors,
652 Opts.include,
653 Opts.omit,
654 Opts.precision,
655 Opts.sort,
656 Opts.show_missing,
657 Opts.skip_covered,
658 Opts.no_skip_covered,
659 Opts.skip_empty,
660 ]
661 + GLOBAL_ARGS,
662 usage="[options] [modules]",
663 description="Report coverage statistics on modules.",
664 ),
665 "run": CmdOptionParser(
666 "run",
667 [
668 Opts.append,
669 Opts.branch,
670 Opts.concurrency,
671 Opts.context,
672 Opts.datafile_output,
673 Opts.include,
674 Opts.module,
675 Opts.omit,
676 Opts.pylib,
677 Opts.parallel_mode,
678 Opts.save_signal,
679 Opts.source,
680 Opts.timid,
681 ]
682 + GLOBAL_ARGS,
683 usage="[options] <pyfile> [program options]",
684 description="Run a Python program, measuring code execution.",
685 ),
686 "xml": CmdOptionParser(
687 "xml",
688 [
689 Opts.datafle_input,
690 Opts.fail_under,
691 Opts.ignore_errors,
692 Opts.include,
693 Opts.omit,
694 Opts.output_xml,
695 Opts.quiet,
696 Opts.skip_empty,
697 ]
698 + GLOBAL_ARGS,
699 usage="[options] [modules]",
700 description="Generate an XML report of coverage results.",
701 ),
702}
705def show_help( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw
706 error: str | None = None,
707 topic: str | None = None,
708 parser: optparse.OptionParser | None = None,
709) -> None:
710 """Display an error message, or the named topic."""
711 assert error or topic or parser 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
713 program_path = sys.argv[0] 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
714 if program_path.endswith(os.path.sep + "__main__.py"): 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
715 # The path is the main module of a package; get that path instead.
716 program_path = os.path.dirname(program_path) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
717 program_name = os.path.basename(program_path) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
718 if env.WINDOWS: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
719 # entry_points={"console_scripts":...} on Windows makes files
720 # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These
721 # invoke coverage-script.py, coverage3-script.py, and
722 # coverage-3.5-script.py. argv[0] is the .py file, but we want to
723 # get back to the original form.
724 auto_suffix = "-script.py" 1jakblcmdneoftpguqhvriws
725 if program_name.endswith(auto_suffix): 725 ↛ 726line 725 didn't jump to line 726 because the condition on line 725 was never true1jakblcmdneoftpguqhvriws
726 program_name = program_name[: -len(auto_suffix)]
728 help_params = dict(coverage.__dict__) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
729 help_params["__url__"] = __url__ 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
730 help_params["program_name"] = program_name 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
731 if CTRACER_FILE: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
732 help_params["extension_modifier"] = "with C extension" 1PQjRSkTUlVWmXYnZ0o12p34q56r
733 else:
734 help_params["extension_modifier"] = "without C extension" 1xyazAbBCcDEdFGeH9I!ftJ#K$guL%M'hvN(O)iw78s
736 if error: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
737 print(error, file=sys.stderr) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
738 print(f"Use '{program_name} help' for help.", file=sys.stderr) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
739 elif parser: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
740 print(parser.format_help().strip()) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
741 print() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
742 else:
743 assert topic is not None 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
744 help_msg = textwrap.dedent(HELP_TOPICS.get(topic, "")).strip() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
745 if help_msg: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
746 print(help_msg.format(**help_params)) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
747 else:
748 print(f"Don't know topic {topic!r}") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
749 print("Full documentation is at {__url__}".format(**help_params)) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
752OK, ERR, FAIL_UNDER = 0, 1, 2 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
755class CoverageScript: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
756 """The command-line interface to coverage.py."""
758 def __init__(self) -> None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
759 self.global_option = False 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
760 self.coverage: Coverage 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
762 def command_line(self, argv: list[str]) -> int: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
763 """The bulk of the command line interface to coverage.py.
765 `argv` is the argument list to process.
767 Returns 0 if all is well, 1 if something went wrong.
769 """
770 # Collect the command-line options.
771 if not argv: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
772 show_help(topic="minimum_help") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
773 return OK 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
775 # The command syntax we parse depends on the first argument. Global
776 # switch syntax always starts with an option.
777 parser: optparse.OptionParser | None
778 self.global_option = argv[0].startswith("-") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
779 if self.global_option: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
780 parser = GlobalOptionParser() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
781 else:
782 parser = COMMANDS.get(argv[0]) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
783 if not parser: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
784 show_help(f"Unknown command: {argv[0]!r}") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
785 return ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
786 argv = argv[1:] 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
788 ok, options, args = parser.parse_args_ok(argv) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
789 if not ok: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
790 return ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
791 assert options is not None 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
793 # Handle help and version.
794 if self.do_help(options, args, parser): 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
795 return OK 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
797 # Listify the list options.
798 source = unshell_list(options.source) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
799 omit = unshell_list(options.omit) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
800 include = unshell_list(options.include) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
801 debug = unshell_list(options.debug) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
802 contexts = unshell_list(options.contexts) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
804 if options.concurrency is not None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
805 concurrency = options.concurrency.split(",") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
806 else:
807 concurrency = None 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
809 # Do something.
810 self.coverage = Coverage( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
811 data_file=options.data_file or DEFAULT_DATAFILE,
812 data_suffix=options.parallel_mode,
813 cover_pylib=options.pylib,
814 timid=options.timid,
815 branch=options.branch,
816 config_file=options.rcfile,
817 source=source,
818 omit=omit,
819 include=include,
820 debug=debug,
821 concurrency=concurrency,
822 check_preimported=True,
823 context=options.context,
824 messages=not options.quiet,
825 )
827 if options.action == "debug": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
828 return self.do_debug(args) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
830 elif options.action == "erase": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
831 self.coverage.erase() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
832 return OK 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
834 elif options.action == "run": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
835 return self.do_run(options, args) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
837 elif options.action == "combine": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
838 if options.append: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
839 self.coverage.load() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
840 data_paths = args or None 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
841 self.coverage.combine(data_paths, strict=True, keep=bool(options.keep)) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
842 self.coverage.save() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
843 return OK 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
845 # Remaining actions are reporting, with some common options.
846 report_args = dict( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
847 morfs=unglob_args(args),
848 ignore_errors=options.ignore_errors,
849 omit=omit,
850 include=include,
851 contexts=contexts,
852 )
854 # We need to be able to import from the current directory, because
855 # plugins may try to, for example, to read Django settings.
856 sys.path.insert(0, "") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
858 self.coverage.load() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
860 total = None 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
861 if options.action == "report": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
862 total = self.coverage.report( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
863 precision=options.precision,
864 show_missing=options.show_missing,
865 skip_covered=options.skip_covered,
866 skip_empty=options.skip_empty,
867 sort=options.sort,
868 output_format=options.format,
869 **report_args,
870 )
871 elif options.action == "annotate": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
872 self.coverage.annotate(directory=options.directory, **report_args) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
873 elif options.action == "html": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
874 total = self.coverage.html_report( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
875 directory=options.directory,
876 precision=options.precision,
877 skip_covered=options.skip_covered,
878 skip_empty=options.skip_empty,
879 show_contexts=options.show_contexts,
880 title=options.title,
881 **report_args,
882 )
883 elif options.action == "xml": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
884 total = self.coverage.xml_report( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
885 outfile=options.outfile,
886 skip_empty=options.skip_empty,
887 **report_args,
888 )
889 elif options.action == "json": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
890 total = self.coverage.json_report( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
891 outfile=options.outfile,
892 pretty_print=options.pretty_print,
893 show_contexts=options.show_contexts,
894 **report_args,
895 )
896 elif options.action == "lcov": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
897 total = self.coverage.lcov_report( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
898 outfile=options.outfile,
899 **report_args,
900 )
901 else:
902 # There are no other possible actions.
903 raise AssertionError
905 if total is not None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
906 # Apply the command line fail-under options, and then use the config
907 # value, so we can get fail_under from the config file.
908 if options.fail_under is not None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
909 self.coverage.set_option("report:fail_under", options.fail_under) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
910 if options.precision is not None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
911 self.coverage.set_option("report:precision", options.precision) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
913 fail_under = cast(float, self.coverage.get_option("report:fail_under")) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
914 precision = cast(int, self.coverage.get_option("report:precision")) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
915 if should_fail_under(total, fail_under, precision): 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
916 msg = "total of {total} is less than fail-under={fail_under:.{p}f}".format( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
917 total=display_covered(total, precision),
918 fail_under=fail_under,
919 p=precision,
920 )
921 print("Coverage failure:", msg) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
922 return FAIL_UNDER 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
924 return OK 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
926 def do_help( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
927 self,
928 options: optparse.Values,
929 args: list[str],
930 parser: optparse.OptionParser,
931 ) -> bool:
932 """Deal with help requests.
934 Return True if it handled the request, False if not.
936 """
937 # Handle help.
938 if options.help: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
939 if self.global_option: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
940 show_help(topic="help") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
941 else:
942 show_help(parser=parser) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
943 return True 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
945 if options.action == "help": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
946 if args: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
947 for a in args: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
948 parser_maybe = COMMANDS.get(a) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
949 if parser_maybe is not None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
950 show_help(parser=parser_maybe) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
951 else:
952 show_help(topic=a) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
953 else:
954 show_help(topic="help") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
955 return True 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
957 # Handle version.
958 if options.version: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
959 show_help(topic="version") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
960 return True 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
962 return False 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
964 def do_signal_save(self, _signum: int, _frame: types.FrameType | None) -> None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
965 """Signal handler to save coverage report"""
966 print("Saving coverage data...", flush=True)
967 self.coverage.save()
969 def do_run(self, options: optparse.Values, args: list[str]) -> int: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
970 """Implementation of 'coverage run'."""
972 if not args: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
973 if options.module: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
974 # Specified -m with nothing else.
975 show_help("No module specified for -m") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
976 return ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
977 command_line = cast(str, self.coverage.get_option("run:command_line")) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
978 if command_line is not None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
979 args = shlex.split(command_line) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
980 if args and args[0] in {"-m", "--module"}: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
981 options.module = True 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
982 args = args[1:] 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
983 if not args: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
984 show_help("Nothing to do.") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
985 return ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
987 if options.append and self.coverage.get_option("run:parallel"): 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
988 show_help("Can't append to data files in parallel mode.") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
989 return ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
991 if options.concurrency == "multiprocessing": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
992 # Can't set other run-affecting command line options with
993 # multiprocessing.
994 for opt_name in ["branch", "include", "omit", "pylib", "source", "timid"]: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
995 # As it happens, all of these options have no default, meaning
996 # they will be None if they have not been specified.
997 if getattr(options, opt_name) is not None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
998 show_help( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
999 "Options affecting multiprocessing must only be specified "
1000 + "in a configuration file.\n"
1001 + f"Remove --{opt_name} from the command line.",
1002 )
1003 return ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1005 os.environ["COVERAGE_RUN"] = "true" 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1007 runner = PyRunner(args, as_module=bool(options.module)) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1008 runner.prepare() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1010 if options.append: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1011 self.coverage.load() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1013 if options.save_signal: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1014 if env.WINDOWS: 1014 ↛ 1017line 1014 didn't jump to line 1017 because the condition on line 1014 was always true1jakblcmdneoftpguqhvriws
1015 show_help("--save-signal is not supported on Windows.") 1jakblcmdneoftpguqhvriws
1016 return ERR 1jakblcmdneoftpguqhvriws
1017 sig = getattr(signal, f"SIG{options.save_signal}")
1018 signal.signal(sig, self.do_signal_save)
1020 # Run the script.
1021 self.coverage.start() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1022 code_ran = True 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1023 try: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1024 runner.run() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1025 except NoSource: 1xyazAbBCcDEdFGeHIfJKgLMhNOi78s
1026 code_ran = False 1xyazAbBCcDEdFGeHIfJKgLMhNOi78s
1027 raise 1xyazAbBCcDEdFGeHIfJKgLMhNOi78s
1028 finally:
1029 self.coverage.stop() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1030 if code_ran: 1030 ↛ 1033line 1030 didn't jump to line 1033 because the condition on line 1030 was always true1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1031 self.coverage.save() 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1033 return OK 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1035 def do_debug(self, args: list[str]) -> int: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1036 """Implementation of 'coverage debug'."""
1038 if not args: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1039 show_help( 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1040 "What information would you like: "
1041 + "config, data, sys, premain, pybehave, sqlite?"
1042 )
1043 return ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1044 if args[1:]: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1045 show_help("Only one topic at a time, please") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1046 return ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1048 if args[0] == "sys": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1049 write_formatted_info(print, "sys", self.coverage.sys_info()) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1050 elif args[0] == "data": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1051 print(info_header("data")) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1052 data_file = self.coverage.config.data_file 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1053 debug_data_file(data_file) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1054 for filename in combinable_files(data_file): 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1055 print("-----") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1056 debug_data_file(filename) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1057 elif args[0] == "config": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1058 write_formatted_info(print, "config", self.coverage.config.debug_info()) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1059 elif args[0] == "premain": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1060 print(info_header("premain")) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1061 print(short_stack(full=True)) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1062 elif args[0] == "pybehave": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1063 write_formatted_info(print, "pybehave", env.debug_info()) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1064 elif args[0] == "sqlite": 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1065 write_formatted_info(print, "sqlite", CoverageData.sys_info()) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1066 else:
1067 show_help(f"Don't know what you mean by {args[0]!r}") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1068 return ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1070 return OK 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1073def unshell_list(s: str) -> list[str] | None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1074 """Turn a command-line argument into a list."""
1075 if not s: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1076 return None 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1077 if env.WINDOWS: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1078 # When running coverage.py as coverage.exe, some of the behavior
1079 # of the shell is emulated: wildcards are expanded into a list of
1080 # file names. So you have to single-quote patterns on the command
1081 # line, but (not) helpfully, the single quotes are included in the
1082 # argument, so we have to strip them off here.
1083 s = s.strip("'") 1jakblcmdneoftpguqhvriws
1084 return s.split(",") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1087def unglob_args(args: list[str]) -> list[str]: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1088 """Interpret shell wildcards for platforms that need it."""
1089 if env.WINDOWS: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1090 globbed = [] 1jakblcmdneoftpguqhvriws
1091 for arg in args: 1jakblcmdneoftpguqhvriws
1092 if "?" in arg or "*" in arg: 1jakblcmdneoftpguqhvriws
1093 globbed.extend(glob.glob(arg)) 1jakblcmdneoftpguqhvriws
1094 else:
1095 globbed.append(arg) 1jakblcmdneoftpguqhvriws
1096 args = globbed 1jakblcmdneoftpguqhvriws
1097 return args 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1100HELP_TOPICS = { 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1101 "help": """\
1102 Coverage.py, version {__version__} {extension_modifier}
1103 Measure, collect, and report on code coverage in Python programs.
1105 usage: {program_name} <command> [options] [args]
1107 Commands:
1108 annotate Annotate source files with execution information.
1109 combine Combine a number of data files.
1110 debug Display information about the internals of coverage.py
1111 erase Erase previously collected coverage data.
1112 help Get help on using coverage.py.
1113 html Create an HTML report.
1114 json Create a JSON report of coverage results.
1115 lcov Create an LCOV report of coverage results.
1116 report Report coverage stats on modules.
1117 run Run a Python program and measure code execution.
1118 xml Create an XML report of coverage results.
1120 Use "{program_name} help <command>" for detailed help on any command.
1121 """,
1122 "minimum_help": oneline(
1123 """
1124 Code coverage for Python, version {__version__} {extension_modifier}.
1125 Use '{program_name} help' for help.
1126 """
1127 ),
1128 "version": "Coverage.py, version {__version__} {extension_modifier}",
1129}
1132def main(argv: list[str] | None = None) -> int | None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1133 """The main entry point to coverage.py.
1135 This is installed as the script entry point.
1137 """
1138 if argv is None: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1139 argv = sys.argv[1:] 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1140 try: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1141 status = CoverageScript().command_line(argv) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1142 except _ExceptionDuringRun as err: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1143 # An exception was caught while running the product code. The
1144 # sys.exc_info() return tuple is packed into an _ExceptionDuringRun
1145 # exception.
1146 traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1147 status = ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1148 except CoverageException as err: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1149 # A controlled error inside coverage.py: print the message to the user.
1150 msg = err.args[0] 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1151 if err.slug: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1152 msg = f"{msg.rstrip('.')}; see {__url__}/messages.html#error-{err.slug}" 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1153 print(msg) 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1154 status = ERR 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1155 except SystemExit as err: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1156 # The user called `sys.exit()`. Exit with their argument, if any.
1157 if err.args: 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1158 status = err.args[0] 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1159 else:
1160 status = None 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH0Iof1J2Kpg3L4Mqh5N6Ori
1161 return status 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1164# Profiling using ox_profile. Install it from GitHub:
1165# pip install git+https://github.com/emin63/ox_profile.git
1166#
1167# $set_env.py: COVERAGE_PROFILE - Set to use ox_profile.
1168_profile = os.getenv("COVERAGE_PROFILE") 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1169if _profile: # pragma: debugging 1PxQyjaRzSAkbTBUClcVDWEmdXFYGneZH90I!oft1J#2K$pgu3L%4M'qhv5N(6O)riw78s
1170 from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error
1172 original_main = main
1174 def main( # pylint: disable=function-redefined
1175 argv: list[str] | None = None,
1176 ) -> int | None:
1177 """A wrapper around main that profiles."""
1178 profiler = SimpleLauncher.launch()
1179 try:
1180 return original_main(argv)
1181 finally:
1182 data, _ = profiler.query(re_filter="coverage", max_records=100)
1183 print(profiler.show(query=data, limit=100, sep="", col=""))
1184 profiler.cancel()