summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore3
-rw-r--r--Makefile3
-rw-r--r--coverage/collector.py30
-rw-r--r--test/coverage_coverage.py34
4 files changed, 67 insertions, 3 deletions
diff --git a/.hgignore b/.hgignore
index 30f28cd..4a860f7 100644
--- a/.hgignore
+++ b/.hgignore
@@ -10,8 +10,9 @@ syntax: glob
# Stuff in the root.
build
-dist
coverage.egg-info
+dist
+htmlcov
MANIFEST
setuptools-*.egg
diff --git a/Makefile b/Makefile
index 0ca6a55..736835e 100644
--- a/Makefile
+++ b/Makefile
@@ -8,8 +8,9 @@ TEST_ZIP = test/zipmods.zip
clean:
python test/test_farm.py clean
-rm -rf build
- -rm -rf dist
-rm -rf coverage.egg-info
+ -rm -rf dist
+ -rm -rf htmlcov
-rm -f *.pyd */*.pyd
-rm -f *.pyc */*.pyc */*/*.pyc */*/*/*.pyc */*/*/*/*.pyc */*/*/*/*/*.pyc
-rm -f *.pyo */*.pyo */*/*.pyo */*/*/*.pyo */*/*/*/*.pyo */*/*/*/*/*.pyo
diff --git a/coverage/collector.py b/coverage/collector.py
index efc41c3..f9bb317 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -56,7 +56,6 @@ except ImportError:
"""Stop this Tracer."""
sys.settrace(None)
-
class Collector:
"""Collects trace data.
@@ -72,6 +71,11 @@ class Collector:
"""
+ # The stack of active Collectors. Collectors are added here when started,
+ # and popped when stopped. Collectors on the stack are paused when not
+ # the top, and resumed when they become the top again.
+ _collectors = []
+
def __init__(self, should_trace):
"""Create a collector.
@@ -123,6 +127,9 @@ class Collector:
def start(self):
"""Start collecting trace information."""
+ if self._collectors:
+ self._collectors[-1]._pause()
+ self._collectors.append(self)
# Install the tracer on this thread.
self._start_tracer()
# Install our installation tracer in threading, to jump start other
@@ -131,10 +138,31 @@ class Collector:
def stop(self):
"""Stop collecting trace information."""
+ assert self._collectors
+ assert self._collectors[-1] is self
+
for tracer in self.tracers:
tracer.stop()
self.tracers = []
threading.settrace(None)
+
+ # Remove this Collector from the stack, and resume the one underneath
+ # (if any).
+ self._collectors.pop()
+ if self._collectors:
+ self._collectors[-1]._resume()
+
+ def _pause(self):
+ """Stop tracing, but be prepared to _resume."""
+ for tracer in self.tracers:
+ tracer.stop()
+ threading.settrace(None)
+
+ def _resume(self):
+ """Resume tracing after a _pause."""
+ for tracer in self.tracers:
+ tracer.start()
+ threading.settrace(self._installation_trace)
def data_points(self):
"""Return the (filename, lineno) pairs collected."""
diff --git a/test/coverage_coverage.py b/test/coverage_coverage.py
new file mode 100644
index 0000000..f2a47f7
--- /dev/null
+++ b/test/coverage_coverage.py
@@ -0,0 +1,34 @@
+# Coverage-test Coverage itself.
+
+import coverage
+import os, sys
+
+cov = coverage.coverage()
+cov.erase()
+cov.start()
+
+# Re-import coverage to get it coverage tested! I don't understand all the
+# mechanics here, but if I don't carry over the imported modules (in covmods),
+# then things go haywire (os == None eventually).
+covmods = {}
+covdir = os.path.split(coverage.__file__)
+for name, mod in sys.modules.items():
+ if name.startswith('coverage'):
+ if hasattr(mod, '__file__') and mod.__file__.startswith(covdir):
+ covmods[name] = mod
+ del sys.modules[name]
+import coverage
+sys.modules.update(covmods)
+
+# Run nosetests, with the arguments from our command line.
+import nose
+nose.run(sys.argv[1:])
+
+cov.stop()
+cov.save() # TODO: This is needed to get group_collected_data called.
+
+cov.exclude("#pragma: no cover")
+cov.exclude("def __repr__")
+cov.exclude("if __name__ == .__main__.:")
+
+cov.html_report(directory='htmlcov', ignore_errors=True)