diff options
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/misc.py | 25 | ||||
-rw-r--r-- | coverage/parser.py | 24 | ||||
-rw-r--r-- | coverage/results.py | 17 |
3 files changed, 39 insertions, 27 deletions
diff --git a/coverage/misc.py b/coverage/misc.py index 50396d61..44f89772 100644 --- a/coverage/misc.py +++ b/coverage/misc.py @@ -83,19 +83,24 @@ def format_lines(statements, lines): def expensive(fn): - """A decorator to cache the result of an expensive operation. + """A decorator to indicate that a method shouldn't be called more than once. - Only applies to methods with no arguments. + Normally, this does nothing. During testing, this raises an exception if + called more than once. """ - attr = "_cache_" + fn.__name__ - - def _wrapped(self): - """Inner function that checks the cache.""" - if not hasattr(self, attr): - setattr(self, attr, fn(self)) - return getattr(self, attr) - return _wrapped + if env.TESTING: + attr = "_once_" + fn.__name__ + + def _wrapped(self): + """Inner function that checks the cache.""" + if hasattr(self, attr): + raise Exception("Shouldn't have called %s more than once" % fn.__name__) + setattr(self, attr, True) + return fn(self) + return _wrapped + else: + return fn def bool_or_none(b): diff --git a/coverage/parser.py b/coverage/parser.py index bc79a30a..dc067157 100644 --- a/coverage/parser.py +++ b/coverage/parser.py @@ -62,8 +62,9 @@ class PythonParser(object): # The line numbers that start statements. self.statement_starts = set() - # Lazily-created ByteParser + # Lazily-created ByteParser and arc data. self._byte_parser = None + self._all_arcs = None @property def byte_parser(self): @@ -232,13 +233,18 @@ class PythonParser(object): normalized to the first line of multi-line statements. """ - all_arcs = [] - for l1, l2 in self.byte_parser._all_arcs(): - fl1 = self.first_line(l1) - fl2 = self.first_line(l2) - if fl1 != fl2: - all_arcs.append((fl1, fl2)) - return all_arcs + return self.arcs_internal() + + def arcs_internal(self): + """Internal worker to calculate the arcs.""" + if self._all_arcs is None: + self._all_arcs = [] + for l1, l2 in self.byte_parser._all_arcs(): + fl1 = self.first_line(l1) + fl2 = self.first_line(l2) + if fl1 != fl2: + self._all_arcs.append((fl1, fl2)) + return self._all_arcs @expensive def exit_counts(self): @@ -249,7 +255,7 @@ class PythonParser(object): """ excluded_lines = self.first_lines(self.excluded) exit_counts = collections.defaultdict(int) - for l1, l2 in self.arcs(): + for l1, l2 in self.arcs_internal(): if l1 < 0: # Don't ever report -1 as a line number continue diff --git a/coverage/results.py b/coverage/results.py index b2848d02..9627373d 100644 --- a/coverage/results.py +++ b/coverage/results.py @@ -25,6 +25,8 @@ class Analysis(object): self.missing = self.statements - executed 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() @@ -33,8 +35,10 @@ class Analysis(object): ) n_missing_branches = sum(len(v) for k,v in iitems(mba)) else: - n_branches = n_partial_branches = n_missing_branches = 0 + self._arc_possibilities = [] + self.exit_counts = {} self.no_branch = set() + n_branches = n_partial_branches = n_missing_branches = 0 self.numbers = Numbers( n_files=1, @@ -60,7 +64,7 @@ class Analysis(object): def arc_possibilities(self): """Returns a sorted list of the arcs in the code.""" - return sorted(self.file_reporter.arcs()) + return self._arc_possibilities def arcs_executed(self): """Returns a sorted list of the arcs actually executed in the code.""" @@ -116,13 +120,11 @@ class Analysis(object): def branch_lines(self): """Returns a list of line numbers that have more than one exit.""" - exit_counts = self.file_reporter.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.file_reporter.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. @@ -145,11 +147,10 @@ class Analysis(object): (total_exits, taken_exits). """ - exit_counts = self.file_reporter.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: |