1 """Command-line support for Coverage.""" |
1 """Command-line support for Coverage.""" |
2 |
2 |
3 import optparse, re, sys |
3 import optparse, os, sys, time, traceback |
4 |
4 |
5 from .execfile import run_python_file |
5 from .backward import sorted # pylint: disable=W0622 |
6 from .misc import CoverageException |
6 from .execfile import run_python_file, run_python_module |
|
7 from .misc import CoverageException, ExceptionDuringRun, NoSource |
|
8 from .debug import info_formatter |
7 |
9 |
8 |
10 |
9 class Opts(object): |
11 class Opts(object): |
10 """A namespace class for individual options we'll build parsers from.""" |
12 """A namespace class for individual options we'll build parsers from.""" |
11 |
13 |
12 append = optparse.Option( |
14 append = optparse.make_option( |
13 '-a', '--append', action='store_false', dest="erase_first", |
15 '-a', '--append', action='store_false', dest="erase_first", |
14 help="Append coverage data to .coverage, otherwise it is started " |
16 help="Append coverage data to .coverage, otherwise it is started " |
15 "clean with each run." |
17 "clean with each run." |
16 ) |
18 ) |
17 branch = optparse.Option( |
19 branch = optparse.make_option( |
18 '', '--branch', action='store_true', |
20 '', '--branch', action='store_true', |
19 help="Measure branch coverage in addition to statement coverage." |
21 help="Measure branch coverage in addition to statement coverage." |
20 ) |
22 ) |
21 directory = optparse.Option( |
23 debug = optparse.make_option( |
22 '-d', '--directory', action='store', |
24 '', '--debug', action='store', metavar="OPTS", |
23 metavar="DIR", |
25 help="Debug options, separated by commas" |
|
26 ) |
|
27 directory = optparse.make_option( |
|
28 '-d', '--directory', action='store', metavar="DIR", |
24 help="Write the output files to DIR." |
29 help="Write the output files to DIR." |
25 ) |
30 ) |
26 help = optparse.Option( |
31 fail_under = optparse.make_option( |
|
32 '', '--fail-under', action='store', metavar="MIN", type="int", |
|
33 help="Exit with a status of 2 if the total coverage is less than MIN." |
|
34 ) |
|
35 help = optparse.make_option( |
27 '-h', '--help', action='store_true', |
36 '-h', '--help', action='store_true', |
28 help="Get help on this command." |
37 help="Get help on this command." |
29 ) |
38 ) |
30 ignore_errors = optparse.Option( |
39 ignore_errors = optparse.make_option( |
31 '-i', '--ignore-errors', action='store_true', |
40 '-i', '--ignore-errors', action='store_true', |
32 help="Ignore errors while reading source files." |
41 help="Ignore errors while reading source files." |
33 ) |
42 ) |
34 pylib = optparse.Option( |
43 include = optparse.make_option( |
|
44 '', '--include', action='store', |
|
45 metavar="PAT1,PAT2,...", |
|
46 help="Include files only when their filename path matches one of " |
|
47 "these patterns. Usually needs quoting on the command line." |
|
48 ) |
|
49 pylib = optparse.make_option( |
35 '-L', '--pylib', action='store_true', |
50 '-L', '--pylib', action='store_true', |
36 help="Measure coverage even inside the Python installed library, " |
51 help="Measure coverage even inside the Python installed library, " |
37 "which isn't done by default." |
52 "which isn't done by default." |
38 ) |
53 ) |
39 show_missing = optparse.Option( |
54 show_missing = optparse.make_option( |
40 '-m', '--show-missing', action='store_true', |
55 '-m', '--show-missing', action='store_true', |
41 help="Show line numbers of statements in each module that weren't " |
56 help="Show line numbers of statements in each module that weren't " |
42 "executed." |
57 "executed." |
43 ) |
58 ) |
44 old_omit = optparse.Option( |
59 old_omit = optparse.make_option( |
45 '-o', '--omit', action='store', |
60 '-o', '--omit', action='store', |
46 metavar="PRE1,PRE2,...", |
61 metavar="PAT1,PAT2,...", |
47 help="Omit files when their filename path starts with one of these " |
62 help="Omit files when their filename matches one of these patterns. " |
48 "prefixes." |
63 "Usually needs quoting on the command line." |
49 ) |
64 ) |
50 omit = optparse.Option( |
65 omit = optparse.make_option( |
51 '', '--omit', action='store', |
66 '', '--omit', action='store', |
52 metavar="PRE1,PRE2,...", |
67 metavar="PAT1,PAT2,...", |
53 help="Omit files when their filename path starts with one of these " |
68 help="Omit files when their filename matches one of these patterns. " |
54 "prefixes." |
69 "Usually needs quoting on the command line." |
55 ) |
70 ) |
56 output_xml = optparse.Option( |
71 output_xml = optparse.make_option( |
57 '-o', '', action='store', dest="outfile", |
72 '-o', '', action='store', dest="outfile", |
58 metavar="OUTFILE", |
73 metavar="OUTFILE", |
59 help="Write the XML report to this file. Defaults to 'coverage.xml'" |
74 help="Write the XML report to this file. Defaults to 'coverage.xml'" |
60 ) |
75 ) |
61 parallel_mode = optparse.Option( |
76 parallel_mode = optparse.make_option( |
62 '-p', '--parallel-mode', action='store_true', |
77 '-p', '--parallel-mode', action='store_true', |
63 help="Include the machine name and process id in the .coverage " |
78 help="Append the machine name, process id and random number to the " |
64 "data file name." |
79 ".coverage data file name to simplify collecting data from " |
65 ) |
80 "many processes." |
66 timid = optparse.Option( |
81 ) |
|
82 module = optparse.make_option( |
|
83 '-m', '--module', action='store_true', |
|
84 help="<pyfile> is an importable Python module, not a script path, " |
|
85 "to be run as 'python -m' would run it." |
|
86 ) |
|
87 rcfile = optparse.make_option( |
|
88 '', '--rcfile', action='store', |
|
89 help="Specify configuration file. Defaults to '.coveragerc'" |
|
90 ) |
|
91 source = optparse.make_option( |
|
92 '', '--source', action='store', metavar="SRC1,SRC2,...", |
|
93 help="A list of packages or directories of code to be measured." |
|
94 ) |
|
95 timid = optparse.make_option( |
67 '', '--timid', action='store_true', |
96 '', '--timid', action='store_true', |
68 help="Use a simpler but slower trace method. Try this if you get " |
97 help="Use a simpler but slower trace method. Try this if you get " |
69 "seemingly impossible results!" |
98 "seemingly impossible results!" |
70 ) |
99 ) |
71 version = optparse.Option( |
100 title = optparse.make_option( |
|
101 '', '--title', action='store', metavar="TITLE", |
|
102 help="A text string to use as the title on the HTML." |
|
103 ) |
|
104 version = optparse.make_option( |
72 '', '--version', action='store_true', |
105 '', '--version', action='store_true', |
73 help="Display version information and exit." |
106 help="Display version information and exit." |
74 ) |
107 ) |
75 |
108 |
76 |
109 |
195 def __eq__(self, other): |
239 def __eq__(self, other): |
196 # A convenience equality, so that I can put strings in unit test |
240 # A convenience equality, so that I can put strings in unit test |
197 # results, and they will compare equal to objects. |
241 # results, and they will compare equal to objects. |
198 return (other == "<CmdOptionParser:%s>" % self.cmd) |
242 return (other == "<CmdOptionParser:%s>" % self.cmd) |
199 |
243 |
|
244 GLOBAL_ARGS = [ |
|
245 Opts.rcfile, |
|
246 Opts.help, |
|
247 ] |
200 |
248 |
201 CMDS = { |
249 CMDS = { |
202 'annotate': CmdOptionParser("annotate", |
250 'annotate': CmdOptionParser("annotate", |
203 [ |
251 [ |
204 Opts.directory, |
252 Opts.directory, |
205 Opts.ignore_errors, |
253 Opts.ignore_errors, |
206 Opts.omit, |
254 Opts.omit, |
207 Opts.help, |
255 Opts.include, |
208 ], |
256 ] + GLOBAL_ARGS, |
209 usage = "[options] [modules]", |
257 usage = "[options] [modules]", |
210 description = "Make annotated copies of the given files, marking " |
258 description = "Make annotated copies of the given files, marking " |
211 "statements that are executed with > and statements that are " |
259 "statements that are executed with > and statements that are " |
212 "missed with !." |
260 "missed with !." |
213 ), |
261 ), |
214 |
262 |
215 'help': CmdOptionParser("help", [Opts.help], |
263 'combine': CmdOptionParser("combine", GLOBAL_ARGS, |
216 usage = "[command]", |
|
217 description = "Describe how to use coverage.py" |
|
218 ), |
|
219 |
|
220 'html': CmdOptionParser("html", |
|
221 [ |
|
222 Opts.directory, |
|
223 Opts.ignore_errors, |
|
224 Opts.omit, |
|
225 Opts.help, |
|
226 ], |
|
227 usage = "[options] [modules]", |
|
228 description = "Create an HTML report of the coverage of the files. " |
|
229 "Each file gets its own page, with the source decorated to show " |
|
230 "executed, excluded, and missed lines." |
|
231 ), |
|
232 |
|
233 'combine': CmdOptionParser("combine", [Opts.help], |
|
234 usage = " ", |
264 usage = " ", |
235 description = "Combine data from multiple coverage files collected " |
265 description = "Combine data from multiple coverage files collected " |
236 "with 'run -p'. The combined results are written to a single " |
266 "with 'run -p'. The combined results are written to a single " |
237 "file representing the union of the data." |
267 "file representing the union of the data." |
238 ), |
268 ), |
239 |
269 |
240 'debug': CmdOptionParser("debug", [Opts.help], |
270 'debug': CmdOptionParser("debug", GLOBAL_ARGS, |
241 usage = "<topic>", |
271 usage = "<topic>", |
242 description = "Display information on the internals of coverage.py, " |
272 description = "Display information on the internals of coverage.py, " |
243 "for diagnosing problems. " |
273 "for diagnosing problems. " |
244 "Topics are 'data' to show a summary of the collected data, " |
274 "Topics are 'data' to show a summary of the collected data, " |
245 "or 'sys' to show installation information." |
275 "or 'sys' to show installation information." |
246 ), |
276 ), |
247 |
277 |
248 'erase': CmdOptionParser("erase", [Opts.help], |
278 'erase': CmdOptionParser("erase", GLOBAL_ARGS, |
249 usage = " ", |
279 usage = " ", |
250 description = "Erase previously collected coverage data." |
280 description = "Erase previously collected coverage data." |
251 ), |
281 ), |
252 |
282 |
|
283 'help': CmdOptionParser("help", GLOBAL_ARGS, |
|
284 usage = "[command]", |
|
285 description = "Describe how to use coverage.py" |
|
286 ), |
|
287 |
|
288 'html': CmdOptionParser("html", |
|
289 [ |
|
290 Opts.directory, |
|
291 Opts.fail_under, |
|
292 Opts.ignore_errors, |
|
293 Opts.omit, |
|
294 Opts.include, |
|
295 Opts.title, |
|
296 ] + GLOBAL_ARGS, |
|
297 usage = "[options] [modules]", |
|
298 description = "Create an HTML report of the coverage of the files. " |
|
299 "Each file gets its own page, with the source decorated to show " |
|
300 "executed, excluded, and missed lines." |
|
301 ), |
|
302 |
253 'report': CmdOptionParser("report", |
303 'report': CmdOptionParser("report", |
254 [ |
304 [ |
|
305 Opts.fail_under, |
255 Opts.ignore_errors, |
306 Opts.ignore_errors, |
256 Opts.omit, |
307 Opts.omit, |
|
308 Opts.include, |
257 Opts.show_missing, |
309 Opts.show_missing, |
258 Opts.help, |
310 ] + GLOBAL_ARGS, |
259 ], |
|
260 usage = "[options] [modules]", |
311 usage = "[options] [modules]", |
261 description = "Report coverage statistics on modules." |
312 description = "Report coverage statistics on modules." |
262 ), |
313 ), |
263 |
314 |
264 'run': CmdOptionParser("execute", |
315 'run': CmdOptionParser("execute", |
265 [ |
316 [ |
266 Opts.append, |
317 Opts.append, |
267 Opts.branch, |
318 Opts.branch, |
|
319 Opts.debug, |
268 Opts.pylib, |
320 Opts.pylib, |
269 Opts.parallel_mode, |
321 Opts.parallel_mode, |
|
322 Opts.module, |
270 Opts.timid, |
323 Opts.timid, |
271 Opts.help, |
324 Opts.source, |
272 ], |
325 Opts.omit, |
|
326 Opts.include, |
|
327 ] + GLOBAL_ARGS, |
273 defaults = {'erase_first': True}, |
328 defaults = {'erase_first': True}, |
274 cmd = "run", |
329 cmd = "run", |
275 usage = "[options] <pyfile> [program options]", |
330 usage = "[options] <pyfile> [program options]", |
276 description = "Run a Python program, measuring code execution." |
331 description = "Run a Python program, measuring code execution." |
277 ), |
332 ), |
278 |
333 |
279 'xml': CmdOptionParser("xml", |
334 'xml': CmdOptionParser("xml", |
280 [ |
335 [ |
|
336 Opts.fail_under, |
281 Opts.ignore_errors, |
337 Opts.ignore_errors, |
282 Opts.omit, |
338 Opts.omit, |
|
339 Opts.include, |
283 Opts.output_xml, |
340 Opts.output_xml, |
284 Opts.help, |
341 ] + GLOBAL_ARGS, |
285 ], |
|
286 cmd = "xml", |
342 cmd = "xml", |
287 defaults = {'outfile': 'coverage.xml'}, |
|
288 usage = "[options] [modules]", |
343 usage = "[options] [modules]", |
289 description = "Generate an XML report of coverage results." |
344 description = "Generate an XML report of coverage results." |
290 ), |
345 ), |
291 } |
346 } |
292 |
347 |
293 |
348 |
294 OK, ERR = 0, 1 |
349 OK, ERR, FAIL_UNDER = 0, 1, 2 |
295 |
350 |
296 |
351 |
297 class CoverageScript(object): |
352 class CoverageScript(object): |
298 """The command-line interface to Coverage.""" |
353 """The command-line interface to Coverage.""" |
299 |
354 |
300 def __init__(self, _covpkg=None, _run_python_file=None, _help_fn=None): |
355 def __init__(self, _covpkg=None, _run_python_file=None, |
|
356 _run_python_module=None, _help_fn=None): |
301 # _covpkg is for dependency injection, so we can test this code. |
357 # _covpkg is for dependency injection, so we can test this code. |
302 if _covpkg: |
358 if _covpkg: |
303 self.covpkg = _covpkg |
359 self.covpkg = _covpkg |
304 else: |
360 else: |
305 import coverage |
361 from . import coverage |
306 self.covpkg = coverage |
362 self.covpkg = coverage |
307 |
363 |
308 # _run_python_file is for dependency injection also. |
364 # For dependency injection: |
309 self.run_python_file = _run_python_file or run_python_file |
365 self.run_python_file = _run_python_file or run_python_file |
310 |
366 self.run_python_module = _run_python_module or run_python_module |
311 # _help_fn is for dependency injection. |
|
312 self.help_fn = _help_fn or self.help |
367 self.help_fn = _help_fn or self.help |
|
368 self.classic = False |
313 |
369 |
314 self.coverage = None |
370 self.coverage = None |
|
371 |
|
372 def command_line(self, argv): |
|
373 """The bulk of the command line interface to Coverage. |
|
374 |
|
375 `argv` is the argument list to process. |
|
376 |
|
377 Returns 0 if all is well, 1 if something went wrong. |
|
378 |
|
379 """ |
|
380 # Collect the command-line options. |
|
381 if not argv: |
|
382 self.help_fn(topic='minimum_help') |
|
383 return OK |
|
384 |
|
385 # The command syntax we parse depends on the first argument. Classic |
|
386 # syntax always starts with an option. |
|
387 self.classic = argv[0].startswith('-') |
|
388 if self.classic: |
|
389 parser = ClassicOptionParser() |
|
390 else: |
|
391 parser = CMDS.get(argv[0]) |
|
392 if not parser: |
|
393 self.help_fn("Unknown command: '%s'" % argv[0]) |
|
394 return ERR |
|
395 argv = argv[1:] |
|
396 |
|
397 parser.help_fn = self.help_fn |
|
398 ok, options, args = parser.parse_args(argv) |
|
399 if not ok: |
|
400 return ERR |
|
401 |
|
402 # Handle help and version. |
|
403 if self.do_help(options, args, parser): |
|
404 return OK |
|
405 |
|
406 # Check for conflicts and problems in the options. |
|
407 if not self.args_ok(options, args): |
|
408 return ERR |
|
409 |
|
410 # Listify the list options. |
|
411 source = unshell_list(options.source) |
|
412 omit = unshell_list(options.omit) |
|
413 include = unshell_list(options.include) |
|
414 debug = unshell_list(options.debug) |
|
415 |
|
416 # Do something. |
|
417 self.coverage = self.covpkg.coverage( |
|
418 data_suffix = options.parallel_mode, |
|
419 cover_pylib = options.pylib, |
|
420 timid = options.timid, |
|
421 branch = options.branch, |
|
422 config_file = options.rcfile, |
|
423 source = source, |
|
424 omit = omit, |
|
425 include = include, |
|
426 debug = debug, |
|
427 ) |
|
428 |
|
429 if 'debug' in options.actions: |
|
430 return self.do_debug(args) |
|
431 |
|
432 if 'erase' in options.actions or options.erase_first: |
|
433 self.coverage.erase() |
|
434 else: |
|
435 self.coverage.load() |
|
436 |
|
437 if 'execute' in options.actions: |
|
438 self.do_execute(options, args) |
|
439 |
|
440 if 'combine' in options.actions: |
|
441 self.coverage.combine() |
|
442 self.coverage.save() |
|
443 |
|
444 # Remaining actions are reporting, with some common options. |
|
445 report_args = dict( |
|
446 morfs = args, |
|
447 ignore_errors = options.ignore_errors, |
|
448 omit = omit, |
|
449 include = include, |
|
450 ) |
|
451 |
|
452 if 'report' in options.actions: |
|
453 total = self.coverage.report( |
|
454 show_missing=options.show_missing, **report_args) |
|
455 if 'annotate' in options.actions: |
|
456 self.coverage.annotate( |
|
457 directory=options.directory, **report_args) |
|
458 if 'html' in options.actions: |
|
459 total = self.coverage.html_report( |
|
460 directory=options.directory, title=options.title, |
|
461 **report_args) |
|
462 if 'xml' in options.actions: |
|
463 outfile = options.outfile |
|
464 total = self.coverage.xml_report(outfile=outfile, **report_args) |
|
465 |
|
466 if options.fail_under is not None: |
|
467 if total >= options.fail_under: |
|
468 return OK |
|
469 else: |
|
470 return FAIL_UNDER |
|
471 else: |
|
472 return OK |
315 |
473 |
316 def help(self, error=None, topic=None, parser=None): |
474 def help(self, error=None, topic=None, parser=None): |
317 """Display an error message, or the named topic.""" |
475 """Display an error message, or the named topic.""" |
318 assert error or topic or parser |
476 assert error or topic or parser |
319 if error: |
477 if error: |
320 print(error) |
478 print(error) |
321 print("Use 'coverage help' for help.") |
479 print("Use 'coverage help' for help.") |
322 elif parser: |
480 elif parser: |
323 print(parser.format_help().strip()) |
481 print(parser.format_help().strip()) |
324 else: |
482 else: |
325 # Parse out the topic we want from HELP_TOPICS |
483 help_msg = HELP_TOPICS.get(topic, '').strip() |
326 topic_list = re.split("(?m)^=+ (\w+) =+$", HELP_TOPICS) |
|
327 topics = dict(zip(topic_list[1::2], topic_list[2::2])) |
|
328 help_msg = topics.get(topic, '').strip() |
|
329 if help_msg: |
484 if help_msg: |
330 print(help_msg % self.covpkg.__dict__) |
485 print(help_msg % self.covpkg.__dict__) |
331 else: |
486 else: |
332 print("Don't know topic %r" % topic) |
487 print("Don't know topic %r" % topic) |
333 |
488 |
334 def command_line(self, argv): |
489 def do_help(self, options, args, parser): |
335 """The bulk of the command line interface to Coverage. |
490 """Deal with help requests. |
336 |
491 |
337 `argv` is the argument list to process. |
492 Return True if it handled the request, False if not. |
338 |
|
339 Returns 0 if all is well, 1 if something went wrong. |
|
340 |
493 |
341 """ |
494 """ |
342 # Collect the command-line options. |
|
343 |
|
344 if not argv: |
|
345 self.help_fn(topic='minimum_help') |
|
346 return OK |
|
347 |
|
348 # The command syntax we parse depends on the first argument. Classic |
|
349 # syntax always starts with an option. |
|
350 classic = argv[0].startswith('-') |
|
351 if classic: |
|
352 parser = ClassicOptionParser() |
|
353 else: |
|
354 parser = CMDS.get(argv[0]) |
|
355 if not parser: |
|
356 self.help_fn("Unknown command: '%s'" % argv[0]) |
|
357 return ERR |
|
358 argv = argv[1:] |
|
359 |
|
360 parser.help_fn = self.help_fn |
|
361 ok, options, args = parser.parse_args(argv) |
|
362 if not ok: |
|
363 return ERR |
|
364 |
|
365 # Handle help. |
495 # Handle help. |
366 if options.help: |
496 if options.help: |
367 if classic: |
497 if self.classic: |
368 self.help_fn(topic='help') |
498 self.help_fn(topic='help') |
369 else: |
499 else: |
370 self.help_fn(parser=parser) |
500 self.help_fn(parser=parser) |
371 return OK |
501 return True |
372 |
502 |
373 if "help" in options.actions: |
503 if "help" in options.actions: |
374 if args: |
504 if args: |
375 for a in args: |
505 for a in args: |
376 parser = CMDS.get(a) |
506 parser = CMDS.get(a) |
378 self.help_fn(parser=parser) |
508 self.help_fn(parser=parser) |
379 else: |
509 else: |
380 self.help_fn(topic=a) |
510 self.help_fn(topic=a) |
381 else: |
511 else: |
382 self.help_fn(topic='help') |
512 self.help_fn(topic='help') |
383 return OK |
513 return True |
384 |
514 |
385 # Handle version. |
515 # Handle version. |
386 if options.version: |
516 if options.version: |
387 self.help_fn(topic='version') |
517 self.help_fn(topic='version') |
388 return OK |
518 return True |
389 |
519 |
390 # Check for conflicts and problems in the options. |
520 return False |
|
521 |
|
522 def args_ok(self, options, args): |
|
523 """Check for conflicts and problems in the options. |
|
524 |
|
525 Returns True if everything is ok, or False if not. |
|
526 |
|
527 """ |
391 for i in ['erase', 'execute']: |
528 for i in ['erase', 'execute']: |
392 for j in ['annotate', 'html', 'report', 'combine']: |
529 for j in ['annotate', 'html', 'report', 'combine']: |
393 if (i in options.actions) and (j in options.actions): |
530 if (i in options.actions) and (j in options.actions): |
394 self.help_fn("You can't specify the '%s' and '%s' " |
531 self.help_fn("You can't specify the '%s' and '%s' " |
395 "options at the same time." % (i, j)) |
532 "options at the same time." % (i, j)) |
396 return ERR |
533 return False |
397 |
534 |
398 if not options.actions: |
535 if not options.actions: |
399 self.help_fn( |
536 self.help_fn( |
400 "You must specify at least one of -e, -x, -c, -r, -a, or -b." |
537 "You must specify at least one of -e, -x, -c, -r, -a, or -b." |
401 ) |
538 ) |
402 return ERR |
539 return False |
403 args_allowed = ( |
540 args_allowed = ( |
404 'execute' in options.actions or |
541 'execute' in options.actions or |
405 'annotate' in options.actions or |
542 'annotate' in options.actions or |
406 'html' in options.actions or |
543 'html' in options.actions or |
407 'debug' in options.actions or |
544 'debug' in options.actions or |
408 'report' in options.actions or |
545 'report' in options.actions or |
409 'xml' in options.actions |
546 'xml' in options.actions |
410 ) |
547 ) |
411 if not args_allowed and args: |
548 if not args_allowed and args: |
412 self.help_fn("Unexpected arguments: %s" % " ".join(args)) |
549 self.help_fn("Unexpected arguments: %s" % " ".join(args)) |
413 return ERR |
550 return False |
414 |
551 |
415 if 'execute' in options.actions and not args: |
552 if 'execute' in options.actions and not args: |
416 self.help_fn("Nothing to do.") |
553 self.help_fn("Nothing to do.") |
|
554 return False |
|
555 |
|
556 return True |
|
557 |
|
558 def do_execute(self, options, args): |
|
559 """Implementation of 'coverage run'.""" |
|
560 |
|
561 # Set the first path element properly. |
|
562 old_path0 = sys.path[0] |
|
563 |
|
564 # Run the script. |
|
565 self.coverage.start() |
|
566 code_ran = True |
|
567 try: |
|
568 try: |
|
569 if options.module: |
|
570 sys.path[0] = '' |
|
571 self.run_python_module(args[0], args) |
|
572 else: |
|
573 filename = args[0] |
|
574 sys.path[0] = os.path.abspath(os.path.dirname(filename)) |
|
575 self.run_python_file(filename, args) |
|
576 except NoSource: |
|
577 code_ran = False |
|
578 raise |
|
579 finally: |
|
580 self.coverage.stop() |
|
581 if code_ran: |
|
582 self.coverage.save() |
|
583 |
|
584 # Restore the old path |
|
585 sys.path[0] = old_path0 |
|
586 |
|
587 def do_debug(self, args): |
|
588 """Implementation of 'coverage debug'.""" |
|
589 |
|
590 if not args: |
|
591 self.help_fn("What information would you like: data, sys?") |
417 return ERR |
592 return ERR |
418 |
593 for info in args: |
419 # Do something. |
594 if info == 'sys': |
420 self.coverage = self.covpkg.coverage( |
595 print("-- sys ----------------------------------------") |
421 data_suffix = bool(options.parallel_mode), |
596 for line in info_formatter(self.coverage.sysinfo()): |
422 cover_pylib = options.pylib, |
597 print(" %s" % line) |
423 timid = options.timid, |
598 elif info == 'data': |
424 branch = options.branch, |
599 print("-- data ---------------------------------------") |
425 ) |
600 self.coverage.load() |
426 |
601 print("path: %s" % self.coverage.data.filename) |
427 if 'debug' in options.actions: |
602 print("has_arcs: %r" % self.coverage.data.has_arcs()) |
428 if not args: |
603 summary = self.coverage.data.summary(fullpath=True) |
429 self.help_fn("What information would you like: data, sys?") |
604 if summary: |
|
605 filenames = sorted(summary.keys()) |
|
606 print("\n%d files:" % len(filenames)) |
|
607 for f in filenames: |
|
608 print("%s: %d lines" % (f, summary[f])) |
|
609 else: |
|
610 print("No data collected") |
|
611 else: |
|
612 self.help_fn("Don't know what you mean by %r" % info) |
430 return ERR |
613 return ERR |
431 for info in args: |
|
432 if info == 'sys': |
|
433 print("-- sys ----------------------------------------") |
|
434 for label, info in self.coverage.sysinfo(): |
|
435 if isinstance(info, list): |
|
436 print("%15s:" % label) |
|
437 for e in info: |
|
438 print("%15s %s" % ("", e)) |
|
439 else: |
|
440 print("%15s: %s" % (label, info)) |
|
441 elif info == 'data': |
|
442 print("-- data ---------------------------------------") |
|
443 self.coverage.load() |
|
444 print("path: %s" % self.coverage.data.filename) |
|
445 print("has_arcs: %r" % self.coverage.data.has_arcs()) |
|
446 summary = self.coverage.data.summary(fullpath=True) |
|
447 if summary: |
|
448 filenames = sorted(summary.keys()) |
|
449 print("\n%d files:" % len(filenames)) |
|
450 for f in filenames: |
|
451 print("%s: %d lines" % (f, summary[f])) |
|
452 else: |
|
453 print("No data collected") |
|
454 else: |
|
455 self.help_fn("Don't know what you mean by %r" % info) |
|
456 return ERR |
|
457 return OK |
|
458 |
|
459 if 'erase' in options.actions or options.erase_first: |
|
460 self.coverage.erase() |
|
461 else: |
|
462 self.coverage.load() |
|
463 |
|
464 if 'execute' in options.actions: |
|
465 # Run the script. |
|
466 self.coverage.start() |
|
467 try: |
|
468 self.run_python_file(args[0], args) |
|
469 finally: |
|
470 self.coverage.stop() |
|
471 self.coverage.save() |
|
472 |
|
473 if 'combine' in options.actions: |
|
474 self.coverage.combine() |
|
475 self.coverage.save() |
|
476 |
|
477 # Remaining actions are reporting, with some common options. |
|
478 report_args = { |
|
479 'morfs': args, |
|
480 'ignore_errors': options.ignore_errors, |
|
481 } |
|
482 |
|
483 omit = None |
|
484 if options.omit: |
|
485 omit = options.omit.split(',') |
|
486 report_args['omit_prefixes'] = omit |
|
487 |
|
488 if 'report' in options.actions: |
|
489 self.coverage.report( |
|
490 show_missing=options.show_missing, **report_args) |
|
491 if 'annotate' in options.actions: |
|
492 self.coverage.annotate( |
|
493 directory=options.directory, **report_args) |
|
494 if 'html' in options.actions: |
|
495 self.coverage.html_report( |
|
496 directory=options.directory, **report_args) |
|
497 if 'xml' in options.actions: |
|
498 outfile = options.outfile |
|
499 if outfile == '-': |
|
500 outfile = None |
|
501 self.coverage.xml_report(outfile=outfile, **report_args) |
|
502 |
|
503 return OK |
614 return OK |
504 |
615 |
505 |
616 |
506 HELP_TOPICS = r""" |
617 def unshell_list(s): |
507 |
618 """Turn a command-line argument into a list.""" |
508 == classic ==================================================================== |
619 if not s: |
509 Coverage.py version %(__version__)s |
620 return None |
|
621 if sys.platform == 'win32': |
|
622 # When running coverage as coverage.exe, some of the behavior |
|
623 # of the shell is emulated: wildcards are expanded into a list of |
|
624 # filenames. So you have to single-quote patterns on the command |
|
625 # line, but (not) helpfully, the single quotes are included in the |
|
626 # argument, so we have to strip them off here. |
|
627 s = s.strip("'") |
|
628 return s.split(',') |
|
629 |
|
630 |
|
631 HELP_TOPICS = { |
|
632 # ------------------------- |
|
633 'classic': |
|
634 r"""Coverage.py version %(__version__)s |
510 Measure, collect, and report on code coverage in Python programs. |
635 Measure, collect, and report on code coverage in Python programs. |
511 |
636 |
512 Usage: |
637 Usage: |
513 |
638 |
514 coverage -x [-p] [-L] [--timid] MODULE.py [ARG1 ARG2 ...] |
639 coverage -x [-p] [-L] [--timid] MODULE.py [ARG1 ARG2 ...] |