DebugClients/Python/coverage/cmdline.py

changeset 6219
d6c795b5ce33
parent 5178
878ce843ca9f
child 6649
f1b3a73831c9
--- a/DebugClients/Python/coverage/cmdline.py	Sat Apr 07 13:17:06 2018 +0200
+++ b/DebugClients/Python/coverage/cmdline.py	Sat Apr 07 13:35:10 2018 +0200
@@ -3,6 +3,8 @@
 
 """Command-line support for coverage.py."""
 
+from __future__ import print_function
+
 import glob
 import optparse
 import os.path
@@ -12,9 +14,10 @@
 
 from coverage import env
 from coverage.collector import CTracer
+from coverage.debug import info_formatter, info_header
 from coverage.execfile import run_python_file, run_python_module
-from coverage.misc import CoverageException, ExceptionDuringRun, NoSource
-from coverage.debug import info_formatter, info_header
+from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource
+from coverage.results import should_fail_under
 
 
 class Opts(object):
@@ -22,7 +25,7 @@
 
     append = optparse.make_option(
         '-a', '--append', action='store_true',
-        help="Append coverage data to .coverage, otherwise it is started clean with each run.",
+        help="Append coverage data to .coverage, otherwise it starts clean each time.",
     )
     branch = optparse.make_option(
         '', '--branch', action='store_true',
@@ -48,7 +51,7 @@
         help="Write the output files to DIR.",
     )
     fail_under = optparse.make_option(
-        '', '--fail-under', action='store', metavar="MIN", type="int",
+        '', '--fail-under', action='store', metavar="MIN", type="float",
         help="Exit with a status of 2 if the total coverage is less than MIN.",
     )
     help = optparse.make_option(
@@ -216,7 +219,7 @@
 class CmdOptionParser(CoverageOptionParser):
     """Parse one of the new-style commands for coverage.py."""
 
-    def __init__(self, action, options=None, defaults=None, usage=None, description=None):
+    def __init__(self, action, options, defaults=None, usage=None, description=None):
         """Create an OptionParser for a coverage.py command.
 
         `action` is the slug to put into `options.action`.
@@ -233,8 +236,7 @@
             description=description,
         )
         self.set_defaults(action=action, **(defaults or {}))
-        if options:
-            self.add_options(options)
+        self.add_options(options)
         self.cmd = action
 
     def __eq__(self, other):
@@ -242,12 +244,14 @@
         # results, and they will compare equal to objects.
         return (other == "<CmdOptionParser:%s>" % 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()
 
         # Include the sub-command for this parser as part of the command.
-        return "%(command)s %(subcommand)s" % {'command': program_name, 'subcommand': self.cmd}
+        return "{command} {subcommand}".format(command=program_name, subcommand=self.cmd)
 
 
 GLOBAL_ARGS = [
@@ -274,8 +278,10 @@
 
     'combine': CmdOptionParser(
         "combine",
-        GLOBAL_ARGS,
-        usage="<path1> <path2> ... <pathN>",
+        [
+            Opts.append,
+            ] + 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 "
@@ -299,7 +305,6 @@
 
     'erase': CmdOptionParser(
         "erase", GLOBAL_ARGS,
-        usage=" ",
         description="Erase previously collected coverage data.",
     ),
 
@@ -318,6 +323,7 @@
             Opts.include,
             Opts.omit,
             Opts.title,
+            Opts.skip_covered,
             ] + GLOBAL_ARGS,
         usage="[options] [modules]",
         description=(
@@ -398,7 +404,11 @@
 
         self.coverage = None
 
-        self.program_name = os.path.basename(sys.argv[0])
+        program_path = sys.argv[0]
+        if program_path.endswith(os.path.sep + '__main__.py'):
+            # The path is the main module of a package; get that path instead.
+            program_path = os.path.dirname(program_path)
+        self.program_name = os.path.basename(program_path)
         if env.WINDOWS:
             # entry_points={'console_scripts':...} on Windows makes files
             # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These
@@ -443,10 +453,6 @@
         if self.do_help(options, args, parser):
             return OK
 
-        # Check for conflicts and problems in the options.
-        if not self.args_ok(options, args):
-            return ERR
-
         # We need to be able to import from the current directory, because
         # plugins may try to, for example, to read Django settings.
         sys.path[0] = ''
@@ -458,7 +464,7 @@
         debug = unshell_list(options.debug)
 
         # Do something.
-        self.coverage = self.covpkg.coverage(
+        self.coverage = self.covpkg.Coverage(
             data_suffix=options.parallel_mode,
             cover_pylib=options.pylib,
             timid=options.timid,
@@ -482,9 +488,10 @@
             return self.do_run(options, args)
 
         elif options.action == "combine":
-            self.coverage.load()
+            if options.append:
+                self.coverage.load()
             data_dirs = args or None
-            self.coverage.combine(data_dirs)
+            self.coverage.combine(data_dirs, strict=True)
             self.coverage.save()
             return OK
 
@@ -509,7 +516,7 @@
         elif options.action == "html":
             total = self.coverage.html_report(
                 directory=options.directory, title=options.title,
-                **report_args)
+                skip_covered=options.skip_covered, **report_args)
         elif options.action == "xml":
             outfile = options.outfile
             total = self.coverage.xml_report(outfile=outfile, **report_args)
@@ -520,20 +527,10 @@
             if options.fail_under is not None:
                 self.coverage.set_option("report:fail_under", options.fail_under)
 
-            if self.coverage.get_option("report:fail_under"):
-
-                # Total needs to be rounded, but be careful of 0 and 100.
-                if 0 < total < 1:
-                    total = 1
-                elif 99 < total < 100:
-                    total = 99
-                else:
-                    total = round(total)
-
-                if total >= self.coverage.get_option("report:fail_under"):
-                    return OK
-                else:
-                    return FAIL_UNDER
+            fail_under = self.coverage.get_option("report:fail_under")
+            precision = self.coverage.get_option("report:precision")
+            if should_fail_under(total, fail_under, precision):
+                return FAIL_UNDER
 
         return OK
 
@@ -541,8 +538,8 @@
         """Display an error message, or the named topic."""
         assert error or topic or parser
         if error:
-            print(error)
-            print("Use '%s help' for help." % (self.program_name,))
+            print(error, file=sys.stderr)
+            print("Use '%s help' for help." % (self.program_name,), file=sys.stderr)
         elif parser:
             print(parser.format_help().strip())
         else:
@@ -591,25 +588,30 @@
 
         return False
 
-    def args_ok(self, options, args):
-        """Check for conflicts and problems in the options.
-
-        Returns True if everything is OK, or False if not.
-
-        """
-        if options.action == "run" and not args:
-            self.help_fn("Nothing to do.")
-            return False
-
-        return True
-
     def do_run(self, options, args):
         """Implementation of 'coverage run'."""
 
+        if not args:
+            self.help_fn("Nothing to do.")
+            return ERR
+
         if options.append and self.coverage.get_option("run:parallel"):
             self.help_fn("Can't append to data files in parallel mode.")
             return ERR
 
+        if options.concurrency == "multiprocessing":
+            # Can't set other run-affecting command line options with
+            # multiprocessing.
+            for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']:
+                # As it happens, all of these options have no default, meaning
+                # they will be None if they have not been specified.
+                if getattr(options, opt_name) is not None:
+                    self.help_fn(
+                        "Options affecting multiprocessing must be specified "
+                        "in a configuration file."
+                    )
+                    return ERR
+
         if not self.coverage.get_option("run:parallel"):
             if not options.append:
                 self.coverage.erase()
@@ -641,7 +643,7 @@
         """Implementation of 'coverage debug'."""
 
         if not args:
-            self.help_fn("What information would you like: data, sys?")
+            self.help_fn("What information would you like: config, data, sys?")
             return ERR
 
         for info in args:
@@ -668,6 +670,11 @@
                         print(line)
                 else:
                     print("No data collected")
+            elif info == 'config':
+                print(info_header("config"))
+                config_info = self.coverage.config.__dict__.items()
+                for line in info_formatter(config_info):
+                    print(" %s" % line)
             else:
                 self.help_fn("Don't know what you mean by %r" % info)
                 return ERR
@@ -748,9 +755,9 @@
         # An exception was caught while running the product code.  The
         # sys.exc_info() return tuple is packed into an ExceptionDuringRun
         # exception.
-        traceback.print_exception(*err.args)
+        traceback.print_exception(*err.args)    # pylint: disable=no-value-for-parameter
         status = ERR
-    except CoverageException as err:
+    except BaseCoverageException as err:
         # A controlled error inside coverage.py: print the message to the user.
         print(err)
         status = ERR

eric ide

mercurial