DebugClients/Python/coverage/cmdline.py

changeset 3497
7f51ab29a1a2
parent 790
2c0ea0163ef4
child 3499
f2d4b02c7e88
equal deleted inserted replaced
3493:71f15675e89f 3497:7f51ab29a1a2
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
87 add_help_option=False, *args, **kwargs 120 add_help_option=False, *args, **kwargs
88 ) 121 )
89 self.set_defaults( 122 self.set_defaults(
90 actions=[], 123 actions=[],
91 branch=None, 124 branch=None,
125 debug=None,
92 directory=None, 126 directory=None,
127 fail_under=None,
93 help=None, 128 help=None,
94 ignore_errors=None, 129 ignore_errors=None,
130 include=None,
95 omit=None, 131 omit=None,
96 parallel_mode=None, 132 parallel_mode=None,
133 module=None,
97 pylib=None, 134 pylib=None,
135 rcfile=True,
98 show_missing=None, 136 show_missing=None,
137 source=None,
99 timid=None, 138 timid=None,
139 title=None,
100 erase_first=None, 140 erase_first=None,
101 version=None, 141 version=None,
102 ) 142 )
103 143
104 self.disable_interspersed_args() 144 self.disable_interspersed_args()
105 self.help_fn = lambda: None 145 self.help_fn = self.help_noop
146
147 def help_noop(self, error=None, topic=None, parser=None):
148 """No-op help function."""
149 pass
106 150
107 class OptionParserError(Exception): 151 class OptionParserError(Exception):
108 """Used to stop the optparse error handler ending the process.""" 152 """Used to stop the optparse error handler ending the process."""
109 pass 153 pass
110 154
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 ...]
548 a directory listed in the omit list. 673 a directory listed in the omit list.
549 e.g. coverage -i -r -o c:\python25,lib\enthought\traits 674 e.g. coverage -i -r -o c:\python25,lib\enthought\traits
550 675
551 Coverage data is saved in the file .coverage by default. Set the 676 Coverage data is saved in the file .coverage by default. Set the
552 COVERAGE_FILE environment variable to save it somewhere else. 677 COVERAGE_FILE environment variable to save it somewhere else.
553 678 """,
554 == help ======================================================================= 679 # -------------------------
680 'help': """\
555 Coverage.py, version %(__version__)s 681 Coverage.py, version %(__version__)s
556 Measure, collect, and report on code coverage in Python programs. 682 Measure, collect, and report on code coverage in Python programs.
557 683
558 usage: coverage <command> [options] [args] 684 usage: coverage <command> [options] [args]
559 685
568 xml Create an XML report of coverage results. 694 xml Create an XML report of coverage results.
569 695
570 Use "coverage help <command>" for detailed help on any command. 696 Use "coverage help <command>" for detailed help on any command.
571 Use "coverage help classic" for help on older command syntax. 697 Use "coverage help classic" for help on older command syntax.
572 For more information, see %(__url__)s 698 For more information, see %(__url__)s
573 699 """,
574 == minimum_help =============================================================== 700 # -------------------------
701 'minimum_help': """\
575 Code coverage for Python. Use 'coverage help' for help. 702 Code coverage for Python. Use 'coverage help' for help.
576 703 """,
577 == version ==================================================================== 704 # -------------------------
705 'version': """\
578 Coverage.py, version %(__version__)s. %(__url__)s 706 Coverage.py, version %(__version__)s. %(__url__)s
579 707 """,
580 """ 708 }
581 709
582 710
583 def main(): 711 def main(argv=None):
584 """The main entrypoint to Coverage. 712 """The main entry point to Coverage.
585 713
586 This is installed as the script entrypoint. 714 This is installed as the script entry point.
587 715
588 """ 716 """
717 if argv is None:
718 argv = sys.argv[1:]
589 try: 719 try:
590 status = CoverageScript().command_line(sys.argv[1:]) 720 start = time.clock()
721 status = CoverageScript().command_line(argv)
722 end = time.clock()
723 if 0:
724 print("time: %.3fs" % (end - start))
725 except ExceptionDuringRun:
726 # An exception was caught while running the product code. The
727 # sys.exc_info() return tuple is packed into an ExceptionDuringRun
728 # exception.
729 _, err, _ = sys.exc_info()
730 traceback.print_exception(*err.args)
731 status = ERR
591 except CoverageException: 732 except CoverageException:
733 # A controlled error inside coverage.py: print the message to the user.
592 _, err, _ = sys.exc_info() 734 _, err, _ = sys.exc_info()
593 print(err) 735 print(err)
594 status = ERR 736 status = ERR
737 except SystemExit:
738 # The user called `sys.exit()`. Exit with their argument, if any.
739 _, err, _ = sys.exc_info()
740 if err.args:
741 status = err.args[0]
742 else:
743 status = None
595 return status 744 return status
596
597 #
598 # eflag: FileType = Python2

eric ide

mercurial