diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2011-05-31 23:12:40 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2011-05-31 23:12:40 -0400 |
commit | 139497ac9cf495a13e9f3323db489bf56054f5d4 (patch) | |
tree | eae49dfbee407d333ef61618351936ded5945297 /coverage | |
parent | 61deec574e9d0181ad986d1276e3d57e8427930d (diff) | |
download | python-coveragepy-git-139497ac9cf495a13e9f3323db489bf56054f5d4.tar.gz |
Better handling of the partial-branch exclusion regexes. Finishes issue #113.
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/config.py | 6 | ||||
-rw-r--r-- | coverage/control.py | 57 | ||||
-rw-r--r-- | coverage/misc.py | 4 | ||||
-rw-r--r-- | coverage/results.py | 11 |
4 files changed, 50 insertions, 28 deletions
diff --git a/coverage/config.py b/coverage/config.py index 4507bc22..f842964d 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -17,10 +17,8 @@ DEFAULT_PARTIAL = [ # These are any Python branching constructs that can't actually execute all # their branches. DEFAULT_PARTIAL_ALWAYS = [ - 'while True:', - 'while 1:', - 'if 0:', - 'if 1:', + 'while (True|1|False|0):', + 'if (True|1|False|0):', ] diff --git a/coverage/control.py b/coverage/control.py index b71887f7..2d964395 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -109,8 +109,10 @@ class coverage(object): self.auto_data = auto_data self.atexit_registered = False - self.exclude_re = "" - self._compile_exclude() + # _exclude_re is a dict mapping exclusion list names to compiled + # regexes. + self._exclude_re = {} + self._exclude_regex_stale() self.file_locator = FileLocator() @@ -395,30 +397,49 @@ class coverage(object): self.collector.reset() self.data.erase() - def clear_exclude(self): + def clear_exclude(self, which='exclude'): """Clear the exclude list.""" - self.config.exclude_list = [] - self.exclude_re = "" + setattr(self.config, which + "_list", []) + self._exclude_regex_stale() - def exclude(self, regex): + def exclude(self, regex, which='exclude'): """Exclude source lines from execution consideration. - `regex` is a regular expression. Lines matching this expression are - not considered executable when reporting code coverage. A list of - regexes is maintained; this function adds a new regex to the list. - Matching any of the regexes excludes a source line. + A number of lists of regular expressions are maintained. Each list + selects lines that are treated differently during reporting. + + `which` determines which list is modified. The "exclude" list selects + lines that are not considered executable at all. The "partial" list + indicates lines with branches that are not taken. + + `regex` is a regular expression. The regex is added to the specified + list. If any of the regexes in the list is found in a line, the line + is marked for special treatment during reporting. """ - self.config.exclude_list.append(regex) - self._compile_exclude() + excl_list = getattr(self.config, which + "_list") + excl_list.append(regex) + self._exclude_regex_stale() + + def _exclude_regex_stale(self): + """Drop all the compiled exclusion regexes, a list was modified.""" + self._exclude_re.clear() - def _compile_exclude(self): - """Build the internal usable form of the exclude list.""" - self.exclude_re = join_regex(self.config.exclude_list) + def _exclude_regex(self, which): + """Return a compiled regex for the given exclusion list.""" + if which not in self._exclude_re: + excl_list = getattr(self.config, which + "_list") + self._exclude_re[which] = join_regex(excl_list) + return self._exclude_re[which] - def get_exclude_list(self): - """Return the list of excluded regex patterns.""" - return self.config.exclude_list + def get_exclude_list(self, which='exclude'): + """Return a list of excluded regex patterns. + + `which` indicates which list is desired. See `exclude` for the lists + that are available, and their meaning. + + """ + return getattr(self.config, which + "_list") def save(self): """Save the collected coverage data to the data file.""" diff --git a/coverage/misc.py b/coverage/misc.py index ec0d0ff7..fd9be857 100644 --- a/coverage/misc.py +++ b/coverage/misc.py @@ -77,8 +77,10 @@ def join_regex(regexes): """Combine a list of regexes into one that matches any of them.""" if len(regexes) > 1: return "(" + ")|(".join(regexes) + ")" - else: + elif regexes: return regexes[0] + else: + return "" class Hasher(object): diff --git a/coverage/results.py b/coverage/results.py index 7b032f18..adfb8f42 100644 --- a/coverage/results.py +++ b/coverage/results.py @@ -25,7 +25,7 @@ class Analysis(object): self.parser = CodeParser( text=source, filename=self.filename, - exclude=self.coverage.exclude_re + exclude=self.coverage._exclude_regex('exclude') ) self.statements, self.excluded = self.parser.parse_source() @@ -70,8 +70,6 @@ class Analysis(object): def arc_possibilities(self): """Returns a sorted list of the arcs in the code.""" arcs = self.parser.arcs() - if self.no_branch: - arcs = [(a,b) for (a,b) in arcs if a not in self.no_branch] return arcs def arcs_executed(self): @@ -85,7 +83,11 @@ class Analysis(object): """Returns a sorted list of the arcs in the code not executed.""" possible = self.arc_possibilities() executed = self.arcs_executed() - missing = [p for p in possible if p not in executed] + missing = [ + p for p in possible + if p not in executed + and p[0] not in self.no_branch + ] return sorted(missing) def arcs_unpredicted(self): @@ -99,7 +101,6 @@ class Analysis(object): e for e in executed if e not in possible and e[0] != e[1] - and e[0] not in self.no_branch ] return sorted(unpredicted) |