DebugClients/Python3/coverage/results.py

changeset 4489
d0d6e4ad31bd
parent 3495
fac17a82b431
child 5051
3586ebd9fac8
diff -r 456c58fc64b0 -r d0d6e4ad31bd DebugClients/Python3/coverage/results.py
--- a/DebugClients/Python3/coverage/results.py	Sun Oct 04 13:35:09 2015 +0200
+++ b/DebugClients/Python3/coverage/results.py	Sun Oct 04 22:37:56 2015 +0200
@@ -1,47 +1,44 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
 """Results of coverage measurement."""
 
-import os
+import collections
 
-from .backward import iitems, set, sorted       # pylint: disable=W0622
-from .misc import format_lines, join_regex, NoSource
-from .parser import CodeParser
+from coverage.backward import iitems
+from coverage.misc import format_lines
 
 
 class Analysis(object):
-    """The results of analyzing a code unit."""
-
-    def __init__(self, cov, code_unit):
-        self.coverage = cov
-        self.code_unit = code_unit
+    """The results of analyzing a FileReporter."""
 
-        self.filename = self.code_unit.filename
-        actual_filename, source = self.find_source(self.filename)
-
-        self.parser = CodeParser(
-            text=source, filename=actual_filename,
-            exclude=self.coverage._exclude_regex('exclude')
-            )
-        self.statements, self.excluded = self.parser.parse_source()
+    def __init__(self, data, file_reporter):
+        self.data = data
+        self.file_reporter = file_reporter
+        self.filename = self.file_reporter.filename
+        self.statements = self.file_reporter.lines()
+        self.excluded = self.file_reporter.excluded_lines()
 
         # Identify missing statements.
-        executed = self.coverage.data.executed_lines(self.filename)
-        exec1 = self.parser.first_lines(executed)
-        self.missing = self.statements - exec1
+        executed = self.data.lines(self.filename) or []
+        executed = self.file_reporter.translate_lines(executed)
+        self.missing = self.statements - executed
 
-        if self.coverage.data.has_arcs():
-            self.no_branch = self.parser.lines_matching(
-                join_regex(self.coverage.config.partial_list),
-                join_regex(self.coverage.config.partial_always_list)
-                )
+        if self.data.has_arcs():
+            self._arc_possibilities = sorted(self.file_reporter.arcs())
+            self.exit_counts = self.file_reporter.exit_counts()
+            self.no_branch = self.file_reporter.no_branch_lines()
             n_branches = self.total_branches()
             mba = self.missing_branch_arcs()
             n_partial_branches = sum(
-                [len(v) for k,v in iitems(mba) if k not in self.missing]
+                len(v) for k,v in iitems(mba) if k not in self.missing
                 )
-            n_missing_branches = sum([len(v) for k,v in iitems(mba)])
+            n_missing_branches = sum(len(v) for k,v in iitems(mba))
         else:
+            self._arc_possibilities = []
+            self.exit_counts = {}
+            self.no_branch = set()
             n_branches = n_partial_branches = n_missing_branches = 0
-            self.no_branch = set()
 
         self.numbers = Numbers(
             n_files=1,
@@ -53,44 +50,6 @@
             n_missing_branches=n_missing_branches,
             )
 
-    def find_source(self, filename):
-        """Find the source for `filename`.
-
-        Returns two values: the actual filename, and the source.
-
-        The source returned depends on which of these cases holds:
-
-            * The filename seems to be a non-source file: returns None
-
-            * The filename is a source file, and actually exists: returns None.
-
-            * The filename is a source file, and is in a zip file or egg:
-              returns the source.
-
-            * The filename is a source file, but couldn't be found: raises
-              `NoSource`.
-
-        """
-        source = None
-
-        base, ext = os.path.splitext(filename)
-        TRY_EXTS = {
-            '.py':  ['.py', '.pyw'],
-            '.pyw': ['.pyw'],
-        }
-        try_exts = TRY_EXTS.get(ext)
-        if not try_exts:
-            return filename, None
-
-        for try_ext in try_exts:
-            try_filename = base + try_ext
-            if os.path.exists(try_filename):
-                return try_filename, None
-            source = self.coverage.file_locator.get_zip_data(try_filename)
-            if source:
-                return try_filename, source
-        raise NoSource("No source for code: '%s'" % filename)
-
     def missing_formatted(self):
         """The missing line numbers, formatted nicely.
 
@@ -101,31 +60,47 @@
 
     def has_arcs(self):
         """Were arcs measured in this result?"""
-        return self.coverage.data.has_arcs()
+        return self.data.has_arcs()
 
     def arc_possibilities(self):
         """Returns a sorted list of the arcs in the code."""
-        arcs = self.parser.arcs()
-        return arcs
+        return self._arc_possibilities
 
     def arcs_executed(self):
         """Returns a sorted list of the arcs actually executed in the code."""
-        executed = self.coverage.data.executed_arcs(self.filename)
-        m2fl = self.parser.first_line
-        executed = [(m2fl(l1), m2fl(l2)) for (l1,l2) in executed]
+        executed = self.data.arcs(self.filename) or []
+        executed = self.file_reporter.translate_arcs(executed)
         return sorted(executed)
 
     def arcs_missing(self):
         """Returns a sorted list of the arcs in the code not executed."""
         possible = self.arc_possibilities()
         executed = self.arcs_executed()
-        missing = [
+        missing = (
             p for p in possible
                 if p not in executed
                     and p[0] not in self.no_branch
-            ]
+        )
         return sorted(missing)
 
+    def arcs_missing_formatted(self):
+        """ The missing branch arcs, formatted nicely.
+
+        Returns a string like "1->2, 1->3, 16->20". Omits any mention of
+        branches from missing lines, so if line 17 is missing, then 17->18
+        won't be included.
+
+        """
+        arcs = self.missing_branch_arcs()
+        missing = self.missing
+        line_exits = sorted(iitems(arcs))
+        pairs = []
+        for line, exits in line_exits:
+            for ex in sorted(exits):
+                if line not in missing:
+                    pairs.append('%d->%d' % (line, ex))
+        return ', '.join(pairs)
+
     def arcs_unpredicted(self):
         """Returns a sorted list of the executed arcs missing from the code."""
         possible = self.arc_possibilities()
@@ -133,22 +108,23 @@
         # Exclude arcs here which connect a line to itself.  They can occur
         # in executed data in some cases.  This is where they can cause
         # trouble, and here is where it's the least burden to remove them.
-        unpredicted = [
+        # Also, generators can somehow cause arcs from "enter" to "exit", so
+        # make sure we have at least one positive value.
+        unpredicted = (
             e for e in executed
                 if e not in possible
                     and e[0] != e[1]
-            ]
+                    and (e[0] > 0 or e[1] > 0)
+        )
         return sorted(unpredicted)
 
     def branch_lines(self):
         """Returns a list of line numbers that have more than one exit."""
-        exit_counts = self.parser.exit_counts()
-        return [l1 for l1,count in iitems(exit_counts) if count > 1]
+        return [l1 for l1,count in iitems(self.exit_counts) if count > 1]
 
     def total_branches(self):
         """How many total branches are there?"""
-        exit_counts = self.parser.exit_counts()
-        return sum([count for count in exit_counts.values() if count > 1])
+        return sum(count for count in self.exit_counts.values() if count > 1)
 
     def missing_branch_arcs(self):
         """Return arcs that weren't executed from branch lines.
@@ -158,11 +134,9 @@
         """
         missing = self.arcs_missing()
         branch_lines = set(self.branch_lines())
-        mba = {}
+        mba = collections.defaultdict(list)
         for l1, l2 in missing:
             if l1 in branch_lines:
-                if l1 not in mba:
-                    mba[l1] = []
                 mba[l1].append(l2)
         return mba
 
@@ -173,11 +147,10 @@
         (total_exits, taken_exits).
         """
 
-        exit_counts = self.parser.exit_counts()
         missing_arcs = self.missing_branch_arcs()
         stats = {}
         for lnum in self.branch_lines():
-            exits = exit_counts[lnum]
+            exits = self.exit_counts[lnum]
             try:
                 missing = len(missing_arcs[lnum])
             except KeyError:
@@ -210,35 +183,43 @@
         self.n_partial_branches = n_partial_branches
         self.n_missing_branches = n_missing_branches
 
+    def init_args(self):
+        """Return a list for __init__(*args) to recreate this object."""
+        return [
+            self.n_files, self.n_statements, self.n_excluded, self.n_missing,
+            self.n_branches, self.n_partial_branches, self.n_missing_branches,
+        ]
+
+    @classmethod
     def set_precision(cls, precision):
         """Set the number of decimal places used to report percentages."""
         assert 0 <= precision < 10
         cls._precision = precision
         cls._near0 = 1.0 / 10**precision
         cls._near100 = 100.0 - cls._near0
-    set_precision = classmethod(set_precision)
 
-    def _get_n_executed(self):
+    @property
+    def n_executed(self):
         """Returns the number of executed statements."""
         return self.n_statements - self.n_missing
-    n_executed = property(_get_n_executed)
 
-    def _get_n_executed_branches(self):
+    @property
+    def n_executed_branches(self):
         """Returns the number of executed branches."""
         return self.n_branches - self.n_missing_branches
-    n_executed_branches = property(_get_n_executed_branches)
 
-    def _get_pc_covered(self):
+    @property
+    def pc_covered(self):
         """Returns a single percentage value for coverage."""
         if self.n_statements > 0:
-            pc_cov = (100.0 * (self.n_executed + self.n_executed_branches) /
-                        (self.n_statements + self.n_branches))
+            numerator, denominator = self.ratio_covered
+            pc_cov = (100.0 * numerator) / denominator
         else:
             pc_cov = 100.0
         return pc_cov
-    pc_covered = property(_get_pc_covered)
 
-    def _get_pc_covered_str(self):
+    @property
+    def pc_covered_str(self):
         """Returns the percent covered, as a string, without a percent sign.
 
         Note that "0" is only returned when the value is truly zero, and "100"
@@ -254,15 +235,21 @@
         else:
             pc = round(pc, self._precision)
         return "%.*f" % (self._precision, pc)
-    pc_covered_str = property(_get_pc_covered_str)
 
+    @classmethod
     def pc_str_width(cls):
         """How many characters wide can pc_covered_str be?"""
         width = 3   # "100"
         if cls._precision > 0:
             width += 1 + cls._precision
         return width
-    pc_str_width = classmethod(pc_str_width)
+
+    @property
+    def ratio_covered(self):
+        """Return a numerator and denominator for the coverage ratio."""
+        numerator = self.n_executed + self.n_executed_branches
+        denominator = self.n_statements + self.n_branches
+        return numerator, denominator
 
     def __add__(self, other):
         nums = Numbers()

eric ide

mercurial