eric7/DebugClients/Python/coverage/cmdline.py

branch
eric7
changeset 8775
0802ae193343
parent 8527
2bd1325d727e
child 8929
fcca2fa618bf
--- a/eric7/DebugClients/Python/coverage/cmdline.py	Fri Nov 19 19:28:47 2021 +0100
+++ b/eric7/DebugClients/Python/coverage/cmdline.py	Sat Nov 20 16:47:38 2021 +0100
@@ -3,10 +3,10 @@
 
 """Command-line support for coverage.py."""
 
-from __future__ import print_function
 
 import glob
-import optparse
+import optparse     # pylint: disable=deprecated-module
+import os
 import os.path
 import shlex
 import sys
@@ -19,12 +19,13 @@
 from coverage.collector import CTracer
 from coverage.data import line_counts
 from coverage.debug import info_formatter, info_header, short_stack
+from coverage.exceptions import BaseCoverageException, ExceptionDuringRun, NoSource
 from coverage.execfile import PyRunner
-from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource, output_encoding
-from coverage.results import should_fail_under
+from coverage.misc import human_sorted
+from coverage.results import Numbers, should_fail_under
 
 
-class Opts(object):
+class Opts:
     """A namespace class for individual options we'll build parsers from."""
 
     append = optparse.make_option(
@@ -46,14 +47,22 @@
         '', '--concurrency', action='store', metavar="LIB",
         choices=CONCURRENCY_CHOICES,
         help=(
-            "Properly measure code using a concurrency library. "
-            "Valid values are: %s."
-        ) % ", ".join(CONCURRENCY_CHOICES),
+            "Properly measure code using a concurrency library. " +
+            "Valid values are: {}."
+        ).format(", ".join(CONCURRENCY_CHOICES)),
     )
     context = optparse.make_option(
         '', '--context', action='store', metavar="LABEL",
         help="The context label to record for this coverage run.",
     )
+    contexts = optparse.make_option(
+        '', '--contexts', action='store',
+        metavar="REGEX1,REGEX2,...",
+        help=(
+            "Only display data from lines covered in the given contexts. " +
+            "Accepts Python regexes, which must be quoted."
+        ),
+    )
     debug = optparse.make_option(
         '', '--debug', action='store', metavar="OPTS",
         help="Debug options, separated by commas. [env: COVERAGE_DEBUG]",
@@ -78,58 +87,36 @@
         '', '--include', action='store',
         metavar="PAT1,PAT2,...",
         help=(
-            "Include only files whose paths match one of these patterns. "
+            "Include only files whose paths match one of these patterns. " +
             "Accepts shell-style wildcards, which must be quoted."
         ),
     )
     pylib = optparse.make_option(
         '-L', '--pylib', action='store_true',
         help=(
-            "Measure coverage even inside the Python installed library, "
+            "Measure coverage even inside the Python installed library, " +
             "which isn't done by default."
         ),
     )
-    sort = optparse.make_option(
-        '--sort', action='store', metavar='COLUMN',
-        help="Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. "
-             "Default is name."
-    )
     show_missing = optparse.make_option(
         '-m', '--show-missing', action='store_true',
         help="Show line numbers of statements in each module that weren't executed.",
     )
-    skip_covered = optparse.make_option(
-        '--skip-covered', action='store_true',
-        help="Skip files with 100% coverage.",
-    )
-    no_skip_covered = optparse.make_option(
-        '--no-skip-covered', action='store_false', dest='skip_covered',
-        help="Disable --skip-covered.",
-    )
-    skip_empty = optparse.make_option(
-        '--skip-empty', action='store_true',
-        help="Skip files with no code.",
-    )
-    show_contexts = optparse.make_option(
-        '--show-contexts', action='store_true',
-        help="Show contexts for covered lines.",
+    module = optparse.make_option(
+        '-m', '--module', action='store_true',
+        help=(
+            "<pyfile> is an importable Python module, not a script path, " +
+            "to be run as 'python -m' would run it."
+        ),
     )
     omit = optparse.make_option(
         '', '--omit', action='store',
         metavar="PAT1,PAT2,...",
         help=(
-            "Omit files whose paths match one of these patterns. "
+            "Omit files whose paths match one of these patterns. " +
             "Accepts shell-style wildcards, which must be quoted."
         ),
     )
-    contexts = optparse.make_option(
-        '', '--contexts', action='store',
-        metavar="REGEX1,REGEX2,...",
-        help=(
-            "Only display data from lines covered in the given contexts. "
-            "Accepts Python regexes, which must be quoted."
-        ),
-    )
     output_xml = optparse.make_option(
         '-o', '', action='store', dest="outfile",
         metavar="OUTFILE",
@@ -147,41 +134,59 @@
     parallel_mode = optparse.make_option(
         '-p', '--parallel-mode', action='store_true',
         help=(
-            "Append the machine name, process id and random number to the "
-            ".coverage data file name to simplify collecting data from "
+            "Append the machine name, process id and random number to the " +
+            ".coverage data file name to simplify collecting data from " +
             "many processes."
         ),
     )
-    module = optparse.make_option(
-        '-m', '--module', action='store_true',
-        help=(
-            "<pyfile> is an importable Python module, not a script path, "
-            "to be run as 'python -m' would run it."
-        ),
-    )
     precision = optparse.make_option(
         '', '--precision', action='store', metavar='N', type=int,
         help=(
-            "Number of digits after the decimal point to display for "
+            "Number of digits after the decimal point to display for " +
             "reported coverage percentages."
         ),
     )
+    quiet = optparse.make_option(
+        '-q', '--quiet', action='store_true',
+        help="Don't print messages about what is happening.",
+    )
     rcfile = optparse.make_option(
         '', '--rcfile', action='store',
         help=(
-            "Specify configuration file. "
-            "By default '.coveragerc', 'setup.cfg', 'tox.ini', and "
+            "Specify configuration file. " +
+            "By default '.coveragerc', 'setup.cfg', 'tox.ini', and " +
             "'pyproject.toml' are tried. [env: COVERAGE_RCFILE]"
         ),
     )
+    show_contexts = optparse.make_option(
+        '--show-contexts', action='store_true',
+        help="Show contexts for covered lines.",
+    )
+    skip_covered = optparse.make_option(
+        '--skip-covered', action='store_true',
+        help="Skip files with 100% coverage.",
+    )
+    no_skip_covered = optparse.make_option(
+        '--no-skip-covered', action='store_false', dest='skip_covered',
+        help="Disable --skip-covered.",
+    )
+    skip_empty = optparse.make_option(
+        '--skip-empty', action='store_true',
+        help="Skip files with no code.",
+    )
+    sort = optparse.make_option(
+        '--sort', action='store', metavar='COLUMN',
+        help="Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. " +
+             "Default is name."
+    )
     source = optparse.make_option(
         '', '--source', action='store', metavar="SRC1,SRC2,...",
-        help="A list of packages or directories of code to be measured.",
+        help="A list of directories or importable names of code to measure.",
     )
     timid = optparse.make_option(
         '', '--timid', action='store_true',
         help=(
-            "Use a simpler but slower trace method. Try this if you get "
+            "Use a simpler but slower trace method. Try this if you get " +
             "seemingly impossible results!"
         ),
     )
@@ -195,7 +200,7 @@
     )
 
 
-class CoverageOptionParser(optparse.OptionParser, object):
+class CoverageOptionParser(optparse.OptionParser):
     """Base OptionParser for coverage.py.
 
     Problems don't exit the program.
@@ -204,7 +209,7 @@
     """
 
     def __init__(self, *args, **kwargs):
-        super(CoverageOptionParser, self).__init__(
+        super().__init__(
             add_help_option=False, *args, **kwargs
             )
         self.set_defaults(
@@ -213,6 +218,7 @@
             branch=None,
             concurrency=None,
             context=None,
+            contexts=None,
             debug=None,
             directory=None,
             fail_under=None,
@@ -222,15 +228,15 @@
             keep=None,
             module=None,
             omit=None,
-            contexts=None,
             parallel_mode=None,
             precision=None,
             pylib=None,
+            quiet=None,
             rcfile=True,
+            show_contexts=None,
             show_missing=None,
             skip_covered=None,
             skip_empty=None,
-            show_contexts=None,
             sort=None,
             source=None,
             timid=None,
@@ -251,7 +257,7 @@
 
         """
         try:
-            options, args = super(CoverageOptionParser, self).parse_args(args, options)
+            options, args = super().parse_args(args, options)
         except self.OptionParserError:
             return False, None, None
         return True, options, args
@@ -266,7 +272,7 @@
     """Command-line parser for coverage.py global option arguments."""
 
     def __init__(self):
-        super(GlobalOptionParser, self).__init__()
+        super().__init__()
 
         self.add_options([
             Opts.help,
@@ -289,7 +295,7 @@
         """
         if usage:
             usage = "%prog " + usage
-        super(CmdOptionParser, self).__init__(
+        super().__init__(
             usage=usage,
             description=description,
         )
@@ -300,16 +306,16 @@
     def __eq__(self, other):
         # A convenience equality, so that I can put strings in unit test
         # results, and they will compare equal to objects.
-        return (other == "<CmdOptionParser:%s>" % self.cmd)
+        return (other == f"<CmdOptionParser:{self.cmd}>")
 
     __hash__ = None     # This object doesn't need to be hashed.
 
     def get_prog_name(self):
         """Override of an undocumented function in optparse.OptionParser."""
-        program_name = super(CmdOptionParser, self).get_prog_name()
+        program_name = super().get_prog_name()
 
         # Include the sub-command for this parser as part of the command.
-        return "{command} {subcommand}".format(command=program_name, subcommand=self.cmd)
+        return f"{program_name} {self.cmd}"
 
 
 GLOBAL_ARGS = [
@@ -329,7 +335,7 @@
             ] + GLOBAL_ARGS,
         usage="[options] [modules]",
         description=(
-            "Make annotated copies of the given files, marking statements that are executed "
+            "Make annotated copies of the given files, marking statements that are executed " +
             "with > and statements that are missed with !."
         ),
     ),
@@ -339,14 +345,15 @@
         [
             Opts.append,
             Opts.keep,
+            Opts.quiet,
             ] + GLOBAL_ARGS,
         usage="[options] <path1> <path2> ... <pathN>",
         description=(
-            "Combine data from multiple coverage files collected "
-            "with 'run -p'.  The combined results are written to a single "
-            "file representing the union of the data. The positional "
-            "arguments are data files or directories containing data files. "
-            "If no paths are provided, data files in the default data file's "
+            "Combine data from multiple coverage files collected " +
+            "with 'run -p'.  The combined results are written to a single " +
+            "file representing the union of the data. The positional " +
+            "arguments are data files or directories containing data files. " +
+            "If no paths are provided, data files in the default data file's " +
             "directory are combined."
         ),
     ),
@@ -355,12 +362,12 @@
         "debug", GLOBAL_ARGS,
         usage="<topic>",
         description=(
-            "Display information about the internals of coverage.py, "
-            "for diagnosing problems. "
-            "Topics are: "
-                "'data' to show a summary of the collected data; "
-                "'sys' to show installation information; "
-                "'config' to show the configuration; "
+            "Display information about the internals of coverage.py, " +
+            "for diagnosing problems. " +
+            "Topics are: " +
+                "'data' to show a summary of the collected data; " +
+                "'sys' to show installation information; " +
+                "'config' to show the configuration; " +
                 "'premain' to show what is calling coverage."
         ),
     ),
@@ -386,6 +393,7 @@
             Opts.include,
             Opts.omit,
             Opts.precision,
+            Opts.quiet,
             Opts.show_contexts,
             Opts.skip_covered,
             Opts.no_skip_covered,
@@ -394,8 +402,8 @@
             ] + GLOBAL_ARGS,
         usage="[options] [modules]",
         description=(
-            "Create an HTML report of the coverage of the files.  "
-            "Each file gets its own page, with the source decorated to show "
+            "Create an HTML report of the coverage of the files.  " +
+            "Each file gets its own page, with the source decorated to show " +
             "executed, excluded, and missed lines."
         ),
     ),
@@ -410,6 +418,7 @@
             Opts.omit,
             Opts.output_json,
             Opts.json_pretty_print,
+            Opts.quiet,
             Opts.show_contexts,
             ] + GLOBAL_ARGS,
         usage="[options] [modules]",
@@ -462,6 +471,7 @@
             Opts.include,
             Opts.omit,
             Opts.output_xml,
+            Opts.quiet,
             Opts.skip_empty,
             ] + GLOBAL_ARGS,
         usage="[options] [modules]",
@@ -498,7 +508,7 @@
 
     if error:
         print(error, file=sys.stderr)
-        print("Use '%s help' for help." % (program_name,), file=sys.stderr)
+        print(f"Use '{program_name} help' for help.", file=sys.stderr)
     elif parser:
         print(parser.format_help().strip())
         print()
@@ -507,14 +517,14 @@
         if help_msg:
             print(help_msg.format(**help_params))
         else:
-            print("Don't know topic %r" % topic)
+            print(f"Don't know topic {topic!r}")
     print("Full documentation is at {__url__}".format(**help_params))
 
 
 OK, ERR, FAIL_UNDER = 0, 1, 2
 
 
-class CoverageScript(object):
+class CoverageScript:
     """The command-line interface to coverage.py."""
 
     def __init__(self):
@@ -542,7 +552,7 @@
         else:
             parser = CMDS.get(argv[0])
             if not parser:
-                show_help("Unknown command: '%s'" % argv[0])
+                show_help(f"Unknown command: {argv[0]!r}")
                 return ERR
             argv = argv[1:]
 
@@ -575,6 +585,7 @@
             concurrency=options.concurrency,
             check_preimported=True,
             context=options.context,
+            messages=not options.quiet,
             )
 
         if options.action == "debug":
@@ -646,6 +657,9 @@
                 show_contexts=options.show_contexts,
                 **report_args
             )
+        else:
+            # There are no other possible actions.
+            raise AssertionError
 
         if total is not None:
             # Apply the command line fail-under options, and then use the config
@@ -656,8 +670,10 @@
             fail_under = self.coverage.get_option("report:fail_under")
             precision = self.coverage.get_option("report:precision")
             if should_fail_under(total, fail_under, precision):
-                msg = "total of {total:.{p}f} is less than fail-under={fail_under:.{p}f}".format(
-                    total=total, fail_under=fail_under, p=precision,
+                msg = "total of {total} is less than fail-under={fail_under:.{p}f}".format(
+                    total=Numbers(precision=precision).display_covered(total),
+                    fail_under=fail_under,
+                    p=precision,
                 )
                 print("Coverage failure:", msg)
                 return FAIL_UNDER
@@ -708,7 +724,7 @@
             command_line = self.coverage.get_option("run:command_line")
             if command_line is not None:
                 args = shlex.split(command_line)
-                if args and args[0] == "-m":
+                if args and args[0] in {"-m", "--module"}:
                     options.module = True
                     args = args[1:]
         if not args:
@@ -727,12 +743,14 @@
                 # they will be None if they have not been specified.
                 if getattr(options, opt_name) is not None:
                     show_help(
-                        "Options affecting multiprocessing must only be specified "
-                        "in a configuration file.\n"
-                        "Remove --{} from the command line.".format(opt_name)
+                        "Options affecting multiprocessing must only be specified " +
+                        "in a configuration file.\n" +
+                        f"Remove --{opt_name} from the command line."
                     )
                     return ERR
 
+        os.environ["COVERAGE_RUN"] = "true"
+
         runner = PyRunner(args, as_module=bool(options.module))
         runner.prepare()
 
@@ -766,22 +784,22 @@
                 sys_info = self.coverage.sys_info()
                 print(info_header("sys"))
                 for line in info_formatter(sys_info):
-                    print(" %s" % line)
+                    print(f" {line}")
             elif info == 'data':
                 self.coverage.load()
                 data = self.coverage.get_data()
                 print(info_header("data"))
-                print("path: %s" % data.data_filename())
+                print(f"path: {data.data_filename()}")
                 if data:
-                    print("has_arcs: %r" % data.has_arcs())
+                    print(f"has_arcs: {data.has_arcs()!r}")
                     summary = line_counts(data, fullpath=True)
-                    filenames = sorted(summary.keys())
-                    print("\n%d files:" % len(filenames))
+                    filenames = human_sorted(summary.keys())
+                    print(f"\n{len(filenames)} files:")
                     for f in filenames:
-                        line = "%s: %d lines" % (f, summary[f])
+                        line = f"{f}: {summary[f]} lines"
                         plugin = data.file_tracer(f)
                         if plugin:
-                            line += " [%s]" % plugin
+                            line += f" [{plugin}]"
                         print(line)
                 else:
                     print("No data collected")
@@ -789,12 +807,12 @@
                 print(info_header("config"))
                 config_info = self.coverage.config.__dict__.items()
                 for line in info_formatter(config_info):
-                    print(" %s" % line)
+                    print(f" {line}")
             elif info == "premain":
                 print(info_header("premain"))
                 print(short_stack())
             else:
-                show_help("Don't know what you mean by %r" % info)
+                show_help(f"Don't know what you mean by {info!r}")
                 return ERR
 
         return OK
@@ -878,8 +896,6 @@
     except BaseCoverageException as err:
         # A controlled error inside coverage.py: print the message to the user.
         msg = err.args[0]
-        if env.PY2:
-            msg = msg.encode(output_encoding())
         print(msg)
         status = ERR
     except SystemExit as err:

eric ide

mercurial