eric6/DebugClients/Python/coverage/cmdline.py

changeset 7427
362cd1b6f81a
parent 6942
2602857055c5
child 7702
f8b97639deb5
equal deleted inserted replaced
7426:dc171b1d8261 7427:362cd1b6f81a
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 2 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3 3
4 """Command-line support for coverage.py.""" 4 """Command-line support for coverage.py."""
5 5
6 from __future__ import print_function 6 from __future__ import print_function
7 7
8 import glob 8 import glob
9 import optparse 9 import optparse
10 import os.path 10 import os.path
11 import shlex
11 import sys 12 import sys
12 import textwrap 13 import textwrap
13 import traceback 14 import traceback
14 15
16 import coverage
17 from coverage import Coverage
15 from coverage import env 18 from coverage import env
16 from coverage.collector import CTracer 19 from coverage.collector import CTracer
20 from coverage.data import line_counts
17 from coverage.debug import info_formatter, info_header 21 from coverage.debug import info_formatter, info_header
18 from coverage.execfile import run_python_file, run_python_module 22 from coverage.execfile import PyRunner
19 from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource 23 from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource, output_encoding
20 from coverage.results import should_fail_under 24 from coverage.results import should_fail_under
21 25
22 26
23 class Opts(object): 27 class Opts(object):
24 """A namespace class for individual options we'll build parsers from.""" 28 """A namespace class for individual options we'll build parsers from."""
40 help=( 44 help=(
41 "Properly measure code using a concurrency library. " 45 "Properly measure code using a concurrency library. "
42 "Valid values are: %s." 46 "Valid values are: %s."
43 ) % ", ".join(CONCURRENCY_CHOICES), 47 ) % ", ".join(CONCURRENCY_CHOICES),
44 ) 48 )
49 context = optparse.make_option(
50 '', '--context', action='store', metavar="LABEL",
51 help="The context label to record for this coverage run.",
52 )
45 debug = optparse.make_option( 53 debug = optparse.make_option(
46 '', '--debug', action='store', metavar="OPTS", 54 '', '--debug', action='store', metavar="OPTS",
47 help="Debug options, separated by commas", 55 help="Debug options, separated by commas. [env: COVERAGE_DEBUG]",
48 ) 56 )
49 directory = optparse.make_option( 57 directory = optparse.make_option(
50 '-d', '--directory', action='store', metavar="DIR", 58 '-d', '--directory', action='store', metavar="DIR",
51 help="Write the output files to DIR.", 59 help="Write the output files to DIR.",
52 ) 60 )
83 ) 91 )
84 skip_covered = optparse.make_option( 92 skip_covered = optparse.make_option(
85 '--skip-covered', action='store_true', 93 '--skip-covered', action='store_true',
86 help="Skip files with 100% coverage.", 94 help="Skip files with 100% coverage.",
87 ) 95 )
96 skip_empty = optparse.make_option(
97 '--skip-empty', action='store_true',
98 help="Skip files with no code.",
99 )
100 show_contexts = optparse.make_option(
101 '--show-contexts', action='store_true',
102 help="Show contexts for covered lines.",
103 )
88 omit = optparse.make_option( 104 omit = optparse.make_option(
89 '', '--omit', action='store', 105 '', '--omit', action='store',
90 metavar="PAT1,PAT2,...", 106 metavar="PAT1,PAT2,...",
91 help=( 107 help=(
92 "Omit files whose paths match one of these patterns. " 108 "Omit files whose paths match one of these patterns. "
93 "Accepts shell-style wildcards, which must be quoted." 109 "Accepts shell-style wildcards, which must be quoted."
94 ), 110 ),
95 ) 111 )
112 contexts = optparse.make_option(
113 '', '--contexts', action='store',
114 metavar="REGEX1,REGEX2,...",
115 help=(
116 "Only display data from lines covered in the given contexts. "
117 "Accepts Python regexes, which must be quoted."
118 ),
119 )
96 output_xml = optparse.make_option( 120 output_xml = optparse.make_option(
97 '-o', '', action='store', dest="outfile", 121 '-o', '', action='store', dest="outfile",
98 metavar="OUTFILE", 122 metavar="OUTFILE",
99 help="Write the XML report to this file. Defaults to 'coverage.xml'", 123 help="Write the XML report to this file. Defaults to 'coverage.xml'",
124 )
125 output_json = optparse.make_option(
126 '-o', '', action='store', dest="outfile",
127 metavar="OUTFILE",
128 help="Write the JSON report to this file. Defaults to 'coverage.json'",
129 )
130 json_pretty_print = optparse.make_option(
131 '', '--pretty-print', action='store_true',
132 help="Format the JSON for human readers.",
100 ) 133 )
101 parallel_mode = optparse.make_option( 134 parallel_mode = optparse.make_option(
102 '-p', '--parallel-mode', action='store_true', 135 '-p', '--parallel-mode', action='store_true',
103 help=( 136 help=(
104 "Append the machine name, process id and random number to the " 137 "Append the machine name, process id and random number to the "
114 ), 147 ),
115 ) 148 )
116 rcfile = optparse.make_option( 149 rcfile = optparse.make_option(
117 '', '--rcfile', action='store', 150 '', '--rcfile', action='store',
118 help=( 151 help=(
119 "Specify configuration file. " 152 "Specify configuration file. "
120 "By default '.coveragerc', 'setup.cfg' and 'tox.ini' are tried." 153 "By default '.coveragerc', 'setup.cfg', 'tox.ini', and "
154 "'pyproject.toml' are tried. [env: COVERAGE_RCFILE]"
121 ), 155 ),
122 ) 156 )
123 source = optparse.make_option( 157 source = optparse.make_option(
124 '', '--source', action='store', metavar="SRC1,SRC2,...", 158 '', '--source', action='store', metavar="SRC1,SRC2,...",
125 help="A list of packages or directories of code to be measured.", 159 help="A list of packages or directories of code to be measured.",
126 ) 160 )
127 timid = optparse.make_option( 161 timid = optparse.make_option(
128 '', '--timid', action='store_true', 162 '', '--timid', action='store_true',
129 help=( 163 help=(
130 "Use a simpler but slower trace method. Try this if you get " 164 "Use a simpler but slower trace method. Try this if you get "
131 "seemingly impossible results!" 165 "seemingly impossible results!"
132 ), 166 ),
133 ) 167 )
134 title = optparse.make_option( 168 title = optparse.make_option(
135 '', '--title', action='store', metavar="TITLE", 169 '', '--title', action='store', metavar="TITLE",
156 self.set_defaults( 190 self.set_defaults(
157 action=None, 191 action=None,
158 append=None, 192 append=None,
159 branch=None, 193 branch=None,
160 concurrency=None, 194 concurrency=None,
195 context=None,
161 debug=None, 196 debug=None,
162 directory=None, 197 directory=None,
163 fail_under=None, 198 fail_under=None,
164 help=None, 199 help=None,
165 ignore_errors=None, 200 ignore_errors=None,
166 include=None, 201 include=None,
167 module=None, 202 module=None,
168 omit=None, 203 omit=None,
204 contexts=None,
169 parallel_mode=None, 205 parallel_mode=None,
170 pylib=None, 206 pylib=None,
171 rcfile=True, 207 rcfile=True,
172 show_missing=None, 208 show_missing=None,
173 skip_covered=None, 209 skip_covered=None,
210 skip_empty=None,
211 show_contexts=None,
174 source=None, 212 source=None,
175 timid=None, 213 timid=None,
176 title=None, 214 title=None,
177 version=None, 215 version=None,
178 ) 216 )
179 217
180 self.disable_interspersed_args() 218 self.disable_interspersed_args()
181 self.help_fn = self.help_noop
182
183 def help_noop(self, error=None, topic=None, parser=None):
184 """No-op help function."""
185 pass
186 219
187 class OptionParserError(Exception): 220 class OptionParserError(Exception):
188 """Used to stop the optparse error handler ending the process.""" 221 """Used to stop the optparse error handler ending the process."""
189 pass 222 pass
190 223
193 226
194 (ok, options, args) 227 (ok, options, args)
195 228
196 """ 229 """
197 try: 230 try:
198 options, args = \ 231 options, args = super(CoverageOptionParser, self).parse_args(args, options)
199 super(CoverageOptionParser, self).parse_args(args, options)
200 except self.OptionParserError: 232 except self.OptionParserError:
201 return False, None, None 233 return False, None, None
202 return True, options, args 234 return True, options, args
203 235
204 def error(self, msg): 236 def error(self, msg):
205 """Override optparse.error so sys.exit doesn't get called.""" 237 """Override optparse.error so sys.exit doesn't get called."""
206 self.help_fn(msg) 238 show_help(msg)
207 raise self.OptionParserError 239 raise self.OptionParserError
208 240
209 241
210 class GlobalOptionParser(CoverageOptionParser): 242 class GlobalOptionParser(CoverageOptionParser):
211 """Command-line parser for coverage.py global option arguments.""" 243 """Command-line parser for coverage.py global option arguments."""
318 ), 350 ),
319 351
320 'html': CmdOptionParser( 352 'html': CmdOptionParser(
321 "html", 353 "html",
322 [ 354 [
355 Opts.contexts,
323 Opts.directory, 356 Opts.directory,
324 Opts.fail_under, 357 Opts.fail_under,
325 Opts.ignore_errors, 358 Opts.ignore_errors,
326 Opts.include, 359 Opts.include,
327 Opts.omit, 360 Opts.omit,
361 Opts.show_contexts,
362 Opts.skip_covered,
363 Opts.skip_empty,
328 Opts.title, 364 Opts.title,
329 Opts.skip_covered,
330 ] + GLOBAL_ARGS, 365 ] + GLOBAL_ARGS,
331 usage="[options] [modules]", 366 usage="[options] [modules]",
332 description=( 367 description=(
333 "Create an HTML report of the coverage of the files. " 368 "Create an HTML report of the coverage of the files. "
334 "Each file gets its own page, with the source decorated to show " 369 "Each file gets its own page, with the source decorated to show "
335 "executed, excluded, and missed lines." 370 "executed, excluded, and missed lines."
336 ), 371 ),
337 ), 372 ),
338 373
374 'json': CmdOptionParser(
375 "json",
376 [
377 Opts.contexts,
378 Opts.fail_under,
379 Opts.ignore_errors,
380 Opts.include,
381 Opts.omit,
382 Opts.output_json,
383 Opts.json_pretty_print,
384 Opts.show_contexts,
385 ] + GLOBAL_ARGS,
386 usage="[options] [modules]",
387 description="Generate a JSON report of coverage results."
388 ),
389
339 'report': CmdOptionParser( 390 'report': CmdOptionParser(
340 "report", 391 "report",
341 [ 392 [
393 Opts.contexts,
342 Opts.fail_under, 394 Opts.fail_under,
343 Opts.ignore_errors, 395 Opts.ignore_errors,
344 Opts.include, 396 Opts.include,
345 Opts.omit, 397 Opts.omit,
346 Opts.show_missing, 398 Opts.show_missing,
347 Opts.skip_covered, 399 Opts.skip_covered,
400 Opts.skip_empty,
348 ] + GLOBAL_ARGS, 401 ] + GLOBAL_ARGS,
349 usage="[options] [modules]", 402 usage="[options] [modules]",
350 description="Report coverage statistics on modules." 403 description="Report coverage statistics on modules."
351 ), 404 ),
352 405
354 "run", 407 "run",
355 [ 408 [
356 Opts.append, 409 Opts.append,
357 Opts.branch, 410 Opts.branch,
358 Opts.concurrency, 411 Opts.concurrency,
412 Opts.context,
359 Opts.include, 413 Opts.include,
360 Opts.module, 414 Opts.module,
361 Opts.omit, 415 Opts.omit,
362 Opts.pylib, 416 Opts.pylib,
363 Opts.parallel_mode, 417 Opts.parallel_mode,
381 description="Generate an XML report of coverage results." 435 description="Generate an XML report of coverage results."
382 ), 436 ),
383 } 437 }
384 438
385 439
440 def show_help(error=None, topic=None, parser=None):
441 """Display an error message, or the named topic."""
442 assert error or topic or parser
443
444 program_path = sys.argv[0]
445 if program_path.endswith(os.path.sep + '__main__.py'):
446 # The path is the main module of a package; get that path instead.
447 program_path = os.path.dirname(program_path)
448 program_name = os.path.basename(program_path)
449 if env.WINDOWS:
450 # entry_points={'console_scripts':...} on Windows makes files
451 # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These
452 # invoke coverage-script.py, coverage3-script.py, and
453 # coverage-3.5-script.py. argv[0] is the .py file, but we want to
454 # get back to the original form.
455 auto_suffix = "-script.py"
456 if program_name.endswith(auto_suffix):
457 program_name = program_name[:-len(auto_suffix)]
458
459 help_params = dict(coverage.__dict__)
460 help_params['program_name'] = program_name
461 if CTracer is not None:
462 help_params['extension_modifier'] = 'with C extension'
463 else:
464 help_params['extension_modifier'] = 'without C extension'
465
466 if error:
467 print(error, file=sys.stderr)
468 print("Use '%s help' for help." % (program_name,), file=sys.stderr)
469 elif parser:
470 print(parser.format_help().strip())
471 print()
472 else:
473 help_msg = textwrap.dedent(HELP_TOPICS.get(topic, '')).strip()
474 if help_msg:
475 print(help_msg.format(**help_params))
476 else:
477 print("Don't know topic %r" % topic)
478 print("Full documentation is at {__url__}".format(**help_params))
479
480
386 OK, ERR, FAIL_UNDER = 0, 1, 2 481 OK, ERR, FAIL_UNDER = 0, 1, 2
387 482
388 483
389 class CoverageScript(object): 484 class CoverageScript(object):
390 """The command-line interface to coverage.py.""" 485 """The command-line interface to coverage.py."""
391 486
392 def __init__(self, _covpkg=None, _run_python_file=None, 487 def __init__(self):
393 _run_python_module=None, _help_fn=None, _path_exists=None):
394 # _covpkg is for dependency injection, so we can test this code.
395 if _covpkg:
396 self.covpkg = _covpkg
397 else:
398 import coverage
399 self.covpkg = coverage
400
401 # For dependency injection:
402 self.run_python_file = _run_python_file or run_python_file
403 self.run_python_module = _run_python_module or run_python_module
404 self.help_fn = _help_fn or self.help
405 self.path_exists = _path_exists or os.path.exists
406 self.global_option = False 488 self.global_option = False
407
408 self.coverage = None 489 self.coverage = None
409
410 program_path = sys.argv[0]
411 if program_path.endswith(os.path.sep + '__main__.py'):
412 # The path is the main module of a package; get that path instead.
413 program_path = os.path.dirname(program_path)
414 self.program_name = os.path.basename(program_path)
415 if env.WINDOWS:
416 # entry_points={'console_scripts':...} on Windows makes files
417 # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These
418 # invoke coverage-script.py, coverage3-script.py, and
419 # coverage-3.5-script.py. argv[0] is the .py file, but we want to
420 # get back to the original form.
421 auto_suffix = "-script.py"
422 if self.program_name.endswith(auto_suffix):
423 self.program_name = self.program_name[:-len(auto_suffix)]
424 490
425 def command_line(self, argv): 491 def command_line(self, argv):
426 """The bulk of the command line interface to coverage.py. 492 """The bulk of the command line interface to coverage.py.
427 493
428 `argv` is the argument list to process. 494 `argv` is the argument list to process.
430 Returns 0 if all is well, 1 if something went wrong. 496 Returns 0 if all is well, 1 if something went wrong.
431 497
432 """ 498 """
433 # Collect the command-line options. 499 # Collect the command-line options.
434 if not argv: 500 if not argv:
435 self.help_fn(topic='minimum_help') 501 show_help(topic='minimum_help')
436 return OK 502 return OK
437 503
438 # The command syntax we parse depends on the first argument. Global 504 # The command syntax we parse depends on the first argument. Global
439 # switch syntax always starts with an option. 505 # switch syntax always starts with an option.
440 self.global_option = argv[0].startswith('-') 506 self.global_option = argv[0].startswith('-')
441 if self.global_option: 507 if self.global_option:
442 parser = GlobalOptionParser() 508 parser = GlobalOptionParser()
443 else: 509 else:
444 parser = CMDS.get(argv[0]) 510 parser = CMDS.get(argv[0])
445 if not parser: 511 if not parser:
446 self.help_fn("Unknown command: '%s'" % argv[0]) 512 show_help("Unknown command: '%s'" % argv[0])
447 return ERR 513 return ERR
448 argv = argv[1:] 514 argv = argv[1:]
449 515
450 parser.help_fn = self.help_fn
451 ok, options, args = parser.parse_args_ok(argv) 516 ok, options, args = parser.parse_args_ok(argv)
452 if not ok: 517 if not ok:
453 return ERR 518 return ERR
454 519
455 # Handle help and version. 520 # Handle help and version.
456 if self.do_help(options, args, parser): 521 if self.do_help(options, args, parser):
457 return OK 522 return OK
458
459 # We need to be able to import from the current directory, because
460 # plugins may try to, for example, to read Django settings.
461 sys.path[0] = ''
462 523
463 # Listify the list options. 524 # Listify the list options.
464 source = unshell_list(options.source) 525 source = unshell_list(options.source)
465 omit = unshell_list(options.omit) 526 omit = unshell_list(options.omit)
466 include = unshell_list(options.include) 527 include = unshell_list(options.include)
467 debug = unshell_list(options.debug) 528 debug = unshell_list(options.debug)
529 contexts = unshell_list(options.contexts)
468 530
469 # Do something. 531 # Do something.
470 self.coverage = self.covpkg.Coverage( 532 self.coverage = Coverage(
471 data_suffix=options.parallel_mode, 533 data_suffix=options.parallel_mode,
472 cover_pylib=options.pylib, 534 cover_pylib=options.pylib,
473 timid=options.timid, 535 timid=options.timid,
474 branch=options.branch, 536 branch=options.branch,
475 config_file=options.rcfile, 537 config_file=options.rcfile,
476 source=source, 538 source=source,
477 omit=omit, 539 omit=omit,
478 include=include, 540 include=include,
479 debug=debug, 541 debug=debug,
480 concurrency=options.concurrency, 542 concurrency=options.concurrency,
543 check_preimported=True,
544 context=options.context,
481 ) 545 )
482 546
483 if options.action == "debug": 547 if options.action == "debug":
484 return self.do_debug(args) 548 return self.do_debug(args)
485 549
502 report_args = dict( 566 report_args = dict(
503 morfs=unglob_args(args), 567 morfs=unglob_args(args),
504 ignore_errors=options.ignore_errors, 568 ignore_errors=options.ignore_errors,
505 omit=omit, 569 omit=omit,
506 include=include, 570 include=include,
571 contexts=contexts,
507 ) 572 )
573
574 # We need to be able to import from the current directory, because
575 # plugins may try to, for example, to read Django settings.
576 sys.path.insert(0, '')
508 577
509 self.coverage.load() 578 self.coverage.load()
510 579
511 total = None 580 total = None
512 if options.action == "report": 581 if options.action == "report":
513 total = self.coverage.report( 582 total = self.coverage.report(
514 show_missing=options.show_missing, 583 show_missing=options.show_missing,
515 skip_covered=options.skip_covered, **report_args) 584 skip_covered=options.skip_covered,
585 skip_empty=options.skip_empty,
586 **report_args
587 )
516 elif options.action == "annotate": 588 elif options.action == "annotate":
517 self.coverage.annotate( 589 self.coverage.annotate(directory=options.directory, **report_args)
518 directory=options.directory, **report_args)
519 elif options.action == "html": 590 elif options.action == "html":
520 total = self.coverage.html_report( 591 total = self.coverage.html_report(
521 directory=options.directory, title=options.title, 592 directory=options.directory,
522 skip_covered=options.skip_covered, **report_args) 593 title=options.title,
594 skip_covered=options.skip_covered,
595 skip_empty=options.skip_empty,
596 show_contexts=options.show_contexts,
597 **report_args
598 )
523 elif options.action == "xml": 599 elif options.action == "xml":
524 outfile = options.outfile 600 outfile = options.outfile
525 total = self.coverage.xml_report(outfile=outfile, **report_args) 601 total = self.coverage.xml_report(outfile=outfile, **report_args)
602 elif options.action == "json":
603 outfile = options.outfile
604 total = self.coverage.json_report(
605 outfile=outfile,
606 pretty_print=options.pretty_print,
607 show_contexts=options.show_contexts,
608 **report_args
609 )
526 610
527 if total is not None: 611 if total is not None:
528 # Apply the command line fail-under options, and then use the config 612 # Apply the command line fail-under options, and then use the config
529 # value, so we can get fail_under from the config file. 613 # value, so we can get fail_under from the config file.
530 if options.fail_under is not None: 614 if options.fail_under is not None:
535 if should_fail_under(total, fail_under, precision): 619 if should_fail_under(total, fail_under, precision):
536 return FAIL_UNDER 620 return FAIL_UNDER
537 621
538 return OK 622 return OK
539 623
540 def help(self, error=None, topic=None, parser=None):
541 """Display an error message, or the named topic."""
542 assert error or topic or parser
543 if error:
544 print(error, file=sys.stderr)
545 print("Use '%s help' for help." % (self.program_name,), file=sys.stderr)
546 elif parser:
547 print(parser.format_help().strip())
548 else:
549 help_params = dict(self.covpkg.__dict__)
550 help_params['program_name'] = self.program_name
551 if CTracer is not None:
552 help_params['extension_modifier'] = 'with C extension'
553 else:
554 help_params['extension_modifier'] = 'without C extension'
555 help_msg = textwrap.dedent(HELP_TOPICS.get(topic, '')).strip()
556 if help_msg:
557 print(help_msg.format(**help_params))
558 else:
559 print("Don't know topic %r" % topic)
560
561 def do_help(self, options, args, parser): 624 def do_help(self, options, args, parser):
562 """Deal with help requests. 625 """Deal with help requests.
563 626
564 Return True if it handled the request, False if not. 627 Return True if it handled the request, False if not.
565 628
566 """ 629 """
567 # Handle help. 630 # Handle help.
568 if options.help: 631 if options.help:
569 if self.global_option: 632 if self.global_option:
570 self.help_fn(topic='help') 633 show_help(topic='help')
571 else: 634 else:
572 self.help_fn(parser=parser) 635 show_help(parser=parser)
573 return True 636 return True
574 637
575 if options.action == "help": 638 if options.action == "help":
576 if args: 639 if args:
577 for a in args: 640 for a in args:
578 parser = CMDS.get(a) 641 parser = CMDS.get(a)
579 if parser: 642 if parser:
580 self.help_fn(parser=parser) 643 show_help(parser=parser)
581 else: 644 else:
582 self.help_fn(topic=a) 645 show_help(topic=a)
583 else: 646 else:
584 self.help_fn(topic='help') 647 show_help(topic='help')
585 return True 648 return True
586 649
587 # Handle version. 650 # Handle version.
588 if options.version: 651 if options.version:
589 self.help_fn(topic='version') 652 show_help(topic='version')
590 return True 653 return True
591 654
592 return False 655 return False
593 656
594 def do_run(self, options, args): 657 def do_run(self, options, args):
595 """Implementation of 'coverage run'.""" 658 """Implementation of 'coverage run'."""
596 659
597 if not args: 660 if not args:
598 self.help_fn("Nothing to do.") 661 if options.module:
662 # Specified -m with nothing else.
663 show_help("No module specified for -m")
664 return ERR
665 command_line = self.coverage.get_option("run:command_line")
666 if command_line is not None:
667 args = shlex.split(command_line)
668 if args and args[0] == "-m":
669 options.module = True
670 args = args[1:]
671 if not args:
672 show_help("Nothing to do.")
599 return ERR 673 return ERR
600 674
601 if options.append and self.coverage.get_option("run:parallel"): 675 if options.append and self.coverage.get_option("run:parallel"):
602 self.help_fn("Can't append to data files in parallel mode.") 676 show_help("Can't append to data files in parallel mode.")
603 return ERR 677 return ERR
604 678
605 if options.concurrency == "multiprocessing": 679 if options.concurrency == "multiprocessing":
606 # Can't set other run-affecting command line options with 680 # Can't set other run-affecting command line options with
607 # multiprocessing. 681 # multiprocessing.
608 for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']: 682 for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']:
609 # As it happens, all of these options have no default, meaning 683 # As it happens, all of these options have no default, meaning
610 # they will be None if they have not been specified. 684 # they will be None if they have not been specified.
611 if getattr(options, opt_name) is not None: 685 if getattr(options, opt_name) is not None:
612 self.help_fn( 686 show_help(
613 "Options affecting multiprocessing must be specified " 687 "Options affecting multiprocessing must only be specified "
614 "in a configuration file." 688 "in a configuration file.\n"
689 "Remove --{} from the command line.".format(opt_name)
615 ) 690 )
616 return ERR 691 return ERR
617 692
618 if not self.coverage.get_option("run:parallel"): 693 runner = PyRunner(args, as_module=bool(options.module))
619 if not options.append: 694 runner.prepare()
620 self.coverage.erase() 695
696 if options.append:
697 self.coverage.load()
621 698
622 # Run the script. 699 # Run the script.
623 self.coverage.start() 700 self.coverage.start()
624 code_ran = True 701 code_ran = True
625 try: 702 try:
626 if options.module: 703 runner.run()
627 self.run_python_module(args[0], args)
628 else:
629 filename = args[0]
630 self.run_python_file(filename, args)
631 except NoSource: 704 except NoSource:
632 code_ran = False 705 code_ran = False
633 raise 706 raise
634 finally: 707 finally:
635 self.coverage.stop() 708 self.coverage.stop()
636 if code_ran: 709 if code_ran:
637 if options.append:
638 data_file = self.coverage.get_option("run:data_file")
639 if self.path_exists(data_file):
640 self.coverage.combine(data_paths=[data_file])
641 self.coverage.save() 710 self.coverage.save()
642 711
643 return OK 712 return OK
644 713
645 def do_debug(self, args): 714 def do_debug(self, args):
646 """Implementation of 'coverage debug'.""" 715 """Implementation of 'coverage debug'."""
647 716
648 if not args: 717 if not args:
649 self.help_fn("What information would you like: config, data, sys?") 718 show_help("What information would you like: config, data, sys, premain?")
650 return ERR 719 return ERR
651 720
652 for info in args: 721 for info in args:
653 if info == 'sys': 722 if info == 'sys':
654 sys_info = self.coverage.sys_info() 723 sys_info = self.coverage.sys_info()
655 print(info_header("sys")) 724 print(info_header("sys"))
656 for line in info_formatter(sys_info): 725 for line in info_formatter(sys_info):
657 print(" %s" % line) 726 print(" %s" % line)
658 elif info == 'data': 727 elif info == 'data':
659 self.coverage.load() 728 self.coverage.load()
660 data = self.coverage.data 729 data = self.coverage.get_data()
661 print(info_header("data")) 730 print(info_header("data"))
662 print("path: %s" % self.coverage.data_files.filename) 731 print("path: %s" % self.coverage.get_data().data_filename())
663 if data: 732 if data:
664 print("has_arcs: %r" % data.has_arcs()) 733 print("has_arcs: %r" % data.has_arcs())
665 summary = data.line_counts(fullpath=True) 734 summary = line_counts(data, fullpath=True)
666 filenames = sorted(summary.keys()) 735 filenames = sorted(summary.keys())
667 print("\n%d files:" % len(filenames)) 736 print("\n%d files:" % len(filenames))
668 for f in filenames: 737 for f in filenames:
669 line = "%s: %d lines" % (f, summary[f]) 738 line = "%s: %d lines" % (f, summary[f])
670 plugin = data.file_tracer(f) 739 plugin = data.file_tracer(f)
676 elif info == 'config': 745 elif info == 'config':
677 print(info_header("config")) 746 print(info_header("config"))
678 config_info = self.coverage.config.__dict__.items() 747 config_info = self.coverage.config.__dict__.items()
679 for line in info_formatter(config_info): 748 for line in info_formatter(config_info):
680 print(" %s" % line) 749 print(" %s" % line)
750 elif info == "premain":
751 print(info_header("premain"))
752 from coverage.debug import short_stack
753 print(short_stack())
681 else: 754 else:
682 self.help_fn("Don't know what you mean by %r" % info) 755 show_help("Don't know what you mean by %r" % info)
683 return ERR 756 return ERR
684 757
685 return OK 758 return OK
686 759
687 760
723 annotate Annotate source files with execution information. 796 annotate Annotate source files with execution information.
724 combine Combine a number of data files. 797 combine Combine a number of data files.
725 erase Erase previously collected coverage data. 798 erase Erase previously collected coverage data.
726 help Get help on using coverage.py. 799 help Get help on using coverage.py.
727 html Create an HTML report. 800 html Create an HTML report.
801 json Create a JSON report of coverage results.
728 report Report coverage stats on modules. 802 report Report coverage stats on modules.
729 run Run a Python program and measure code execution. 803 run Run a Python program and measure code execution.
730 xml Create an XML report of coverage results. 804 xml Create an XML report of coverage results.
731 805
732 Use "{program_name} help <command>" for detailed help on any command. 806 Use "{program_name} help <command>" for detailed help on any command.
733 For full documentation, see {__url__}
734 """, 807 """,
735 808
736 'minimum_help': """\ 809 'minimum_help': """\
737 Code coverage for Python. Use '{program_name} help' for help. 810 Code coverage for Python. Use '{program_name} help' for help.
738 """, 811 """,
739 812
740 'version': """\ 813 'version': """\
741 Coverage.py, version {__version__} {extension_modifier} 814 Coverage.py, version {__version__} {extension_modifier}
742 Documentation at {__url__}
743 """, 815 """,
744 } 816 }
745 817
746 818
747 def main(argv=None): 819 def main(argv=None):
760 # exception. 832 # exception.
761 traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter 833 traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter
762 status = ERR 834 status = ERR
763 except BaseCoverageException as err: 835 except BaseCoverageException as err:
764 # A controlled error inside coverage.py: print the message to the user. 836 # A controlled error inside coverage.py: print the message to the user.
765 print(err) 837 msg = err.args[0]
838 if env.PY2:
839 msg = msg.encode(output_encoding())
840 print(msg)
766 status = ERR 841 status = ERR
767 except SystemExit as err: 842 except SystemExit as err:
768 # The user called `sys.exit()`. Exit with their argument, if any. 843 # The user called `sys.exit()`. Exit with their argument, if any.
769 if err.args: 844 if err.args:
770 status = err.args[0] 845 status = err.args[0]
771 else: 846 else:
772 status = None 847 status = None
773 return status 848 return status
849
850 # Profiling using ox_profile. Install it from GitHub:
851 # pip install git+https://github.com/emin63/ox_profile.git
852 #
853 # $set_env.py: COVERAGE_PROFILE - Set to use ox_profile.
854 _profile = os.environ.get("COVERAGE_PROFILE", "")
855 if _profile: # pragma: debugging
856 from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error
857 original_main = main
858
859 def main(argv=None): # pylint: disable=function-redefined
860 """A wrapper around main that profiles."""
861 try:
862 profiler = SimpleLauncher.launch()
863 return original_main(argv)
864 finally:
865 data, _ = profiler.query(re_filter='coverage', max_records=100)
866 print(profiler.show(query=data, limit=100, sep='', col=''))
867 profiler.cancel()

eric ide

mercurial