15 |
15 |
16 import coverage |
16 import coverage |
17 from coverage import Coverage |
17 from coverage import Coverage |
18 from coverage import env |
18 from coverage import env |
19 from coverage.collector import CTracer |
19 from coverage.collector import CTracer |
20 from coverage.data import line_counts |
20 from coverage.config import CoverageConfig |
|
21 from coverage.data import combinable_files, debug_data_file |
21 from coverage.debug import info_formatter, info_header, short_stack |
22 from coverage.debug import info_formatter, info_header, short_stack |
22 from coverage.exceptions import BaseCoverageException, ExceptionDuringRun, NoSource |
23 from coverage.exceptions import _BaseCoverageException, _ExceptionDuringRun, NoSource |
23 from coverage.execfile import PyRunner |
24 from coverage.execfile import PyRunner |
24 from coverage.misc import human_sorted |
|
25 from coverage.results import Numbers, should_fail_under |
25 from coverage.results import Numbers, should_fail_under |
26 |
26 |
27 |
27 |
28 class Opts: |
28 class Opts: |
29 """A namespace class for individual options we'll build parsers from.""" |
29 """A namespace class for individual options we'll build parsers from.""" |
38 ) |
38 ) |
39 branch = optparse.make_option( |
39 branch = optparse.make_option( |
40 '', '--branch', action='store_true', |
40 '', '--branch', action='store_true', |
41 help="Measure branch coverage in addition to statement coverage.", |
41 help="Measure branch coverage in addition to statement coverage.", |
42 ) |
42 ) |
43 CONCURRENCY_CHOICES = [ |
|
44 "thread", "gevent", "greenlet", "eventlet", "multiprocessing", |
|
45 ] |
|
46 concurrency = optparse.make_option( |
43 concurrency = optparse.make_option( |
47 '', '--concurrency', action='store', metavar="LIB", |
44 '', '--concurrency', action='store', metavar="LIBS", |
48 choices=CONCURRENCY_CHOICES, |
|
49 help=( |
45 help=( |
50 "Properly measure code using a concurrency library. " + |
46 "Properly measure code using a concurrency library. " + |
51 "Valid values are: {}." |
47 "Valid values are: {}, or a comma-list of them." |
52 ).format(", ".join(CONCURRENCY_CHOICES)), |
48 ).format(", ".join(sorted(CoverageConfig.CONCURRENCY_CHOICES))), |
53 ) |
49 ) |
54 context = optparse.make_option( |
50 context = optparse.make_option( |
55 '', '--context', action='store', metavar="LABEL", |
51 '', '--context', action='store', metavar="LABEL", |
56 help="The context label to record for this coverage run.", |
52 help="The context label to record for this coverage run.", |
57 ) |
53 ) |
569 omit = unshell_list(options.omit) |
565 omit = unshell_list(options.omit) |
570 include = unshell_list(options.include) |
566 include = unshell_list(options.include) |
571 debug = unshell_list(options.debug) |
567 debug = unshell_list(options.debug) |
572 contexts = unshell_list(options.contexts) |
568 contexts = unshell_list(options.contexts) |
573 |
569 |
|
570 if options.concurrency is not None: |
|
571 concurrency = options.concurrency.split(",") |
|
572 else: |
|
573 concurrency = None |
|
574 |
574 # Do something. |
575 # Do something. |
575 self.coverage = Coverage( |
576 self.coverage = Coverage( |
576 data_suffix=options.parallel_mode, |
577 data_suffix=options.parallel_mode, |
577 cover_pylib=options.pylib, |
578 cover_pylib=options.pylib, |
578 timid=options.timid, |
579 timid=options.timid, |
580 config_file=options.rcfile, |
581 config_file=options.rcfile, |
581 source=source, |
582 source=source, |
582 omit=omit, |
583 omit=omit, |
583 include=include, |
584 include=include, |
584 debug=debug, |
585 debug=debug, |
585 concurrency=options.concurrency, |
586 concurrency=concurrency, |
586 check_preimported=True, |
587 check_preimported=True, |
587 context=options.context, |
588 context=options.context, |
588 messages=not options.quiet, |
589 messages=not options.quiet, |
589 ) |
590 ) |
590 |
591 |
599 return self.do_run(options, args) |
600 return self.do_run(options, args) |
600 |
601 |
601 elif options.action == "combine": |
602 elif options.action == "combine": |
602 if options.append: |
603 if options.append: |
603 self.coverage.load() |
604 self.coverage.load() |
604 data_dirs = args or None |
605 data_paths = args or None |
605 self.coverage.combine(data_dirs, strict=True, keep=bool(options.keep)) |
606 self.coverage.combine(data_paths, strict=True, keep=bool(options.keep)) |
606 self.coverage.save() |
607 self.coverage.save() |
607 return OK |
608 return OK |
608 |
609 |
609 # Remaining actions are reporting, with some common options. |
610 # Remaining actions are reporting, with some common options. |
610 report_args = dict( |
611 report_args = dict( |
776 """Implementation of 'coverage debug'.""" |
777 """Implementation of 'coverage debug'.""" |
777 |
778 |
778 if not args: |
779 if not args: |
779 show_help("What information would you like: config, data, sys, premain?") |
780 show_help("What information would you like: config, data, sys, premain?") |
780 return ERR |
781 return ERR |
781 |
782 if args[1:]: |
782 for info in args: |
783 show_help("Only one topic at a time, please") |
783 if info == 'sys': |
784 return ERR |
784 sys_info = self.coverage.sys_info() |
785 |
785 print(info_header("sys")) |
786 if args[0] == 'sys': |
786 for line in info_formatter(sys_info): |
787 sys_info = self.coverage.sys_info() |
787 print(f" {line}") |
788 print(info_header("sys")) |
788 elif info == 'data': |
789 for line in info_formatter(sys_info): |
789 self.coverage.load() |
790 print(f" {line}") |
790 data = self.coverage.get_data() |
791 elif args[0] == 'data': |
791 print(info_header("data")) |
792 print(info_header("data")) |
792 print(f"path: {data.data_filename()}") |
793 data_file = self.coverage.config.data_file |
793 if data: |
794 debug_data_file(data_file) |
794 print(f"has_arcs: {data.has_arcs()!r}") |
795 for filename in combinable_files(data_file): |
795 summary = line_counts(data, fullpath=True) |
796 print("-----") |
796 filenames = human_sorted(summary.keys()) |
797 debug_data_file(filename) |
797 print(f"\n{len(filenames)} files:") |
798 elif args[0] == 'config': |
798 for f in filenames: |
799 print(info_header("config")) |
799 line = f"{f}: {summary[f]} lines" |
800 config_info = sorted(self.coverage.config.__dict__.items()) |
800 plugin = data.file_tracer(f) |
801 for line in info_formatter(config_info): |
801 if plugin: |
802 print(f" {line}") |
802 line += f" [{plugin}]" |
803 elif args[0] == "premain": |
803 print(line) |
804 print(info_header("premain")) |
804 else: |
805 print(short_stack()) |
805 print("No data collected") |
806 else: |
806 elif info == 'config': |
807 show_help(f"Don't know what you mean by {args[0]!r}") |
807 print(info_header("config")) |
808 return ERR |
808 config_info = self.coverage.config.__dict__.items() |
|
809 for line in info_formatter(config_info): |
|
810 print(f" {line}") |
|
811 elif info == "premain": |
|
812 print(info_header("premain")) |
|
813 print(short_stack()) |
|
814 else: |
|
815 show_help(f"Don't know what you mean by {info!r}") |
|
816 return ERR |
|
817 |
809 |
818 return OK |
810 return OK |
819 |
811 |
820 |
812 |
821 def unshell_list(s): |
813 def unshell_list(s): |
885 """ |
877 """ |
886 if argv is None: |
878 if argv is None: |
887 argv = sys.argv[1:] |
879 argv = sys.argv[1:] |
888 try: |
880 try: |
889 status = CoverageScript().command_line(argv) |
881 status = CoverageScript().command_line(argv) |
890 except ExceptionDuringRun as err: |
882 except _ExceptionDuringRun as err: |
891 # An exception was caught while running the product code. The |
883 # An exception was caught while running the product code. The |
892 # sys.exc_info() return tuple is packed into an ExceptionDuringRun |
884 # sys.exc_info() return tuple is packed into an _ExceptionDuringRun |
893 # exception. |
885 # exception. |
894 traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter |
886 traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter |
895 status = ERR |
887 status = ERR |
896 except BaseCoverageException as err: |
888 except _BaseCoverageException as err: |
897 # A controlled error inside coverage.py: print the message to the user. |
889 # A controlled error inside coverage.py: print the message to the user. |
898 msg = err.args[0] |
890 msg = err.args[0] |
899 print(msg) |
891 print(msg) |
900 status = ERR |
892 status = ERR |
901 except SystemExit as err: |
893 except SystemExit as err: |