summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2011-05-31 23:12:40 -0400
committerNed Batchelder <ned@nedbatchelder.com>2011-05-31 23:12:40 -0400
commit139497ac9cf495a13e9f3323db489bf56054f5d4 (patch)
treeeae49dfbee407d333ef61618351936ded5945297 /coverage
parent61deec574e9d0181ad986d1276e3d57e8427930d (diff)
downloadpython-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.py6
-rw-r--r--coverage/control.py57
-rw-r--r--coverage/misc.py4
-rw-r--r--coverage/results.py11
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)