diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2015-01-30 21:07:25 -0500 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2015-01-30 21:07:25 -0500 |
commit | 6c631d76f22c50220bba51ec3191260d7e74b11f (patch) | |
tree | 13d62e18be0149d11b6f772172b712a5a827ecb3 /coverage | |
parent | cab3262dd815c454e8db967f97afce8a4b071d4c (diff) | |
download | python-coveragepy-git-6c631d76f22c50220bba51ec3191260d7e74b11f.tar.gz |
Wildly experimental multiprocessing support. Covers most of #117.
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/cmdline.py | 4 | ||||
-rw-r--r-- | coverage/control.py | 10 | ||||
-rw-r--r-- | coverage/monkey.py | 45 |
3 files changed, 57 insertions, 2 deletions
diff --git a/coverage/cmdline.py b/coverage/cmdline.py index ff3de4ca..4ef08f1d 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -24,7 +24,9 @@ class Opts(object): '', '--branch', action='store_true', help="Measure branch coverage in addition to statement coverage." ) - CONCURRENCY_CHOICES = ["thread", "gevent", "greenlet", "eventlet"] + CONCURRENCY_CHOICES = [ + "thread", "gevent", "greenlet", "eventlet", "multiprocessing", + ] concurrency = optparse.make_option( '', '--concurrency', action='store', metavar="LIB", choices=CONCURRENCY_CHOICES, diff --git a/coverage/control.py b/coverage/control.py index 319f56dc..f422d7c0 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -21,6 +21,7 @@ from coverage.files import ModuleMatcher from coverage.html import HtmlReporter from coverage.misc import CoverageException, bool_or_none, join_regex from coverage.misc import file_be_gone, overrides +from coverage.monkey import patch_multiprocessing from coverage.plugin import CoveragePlugin, FileReporter from coverage.python import PythonCodeUnit from coverage.results import Analysis, Numbers @@ -219,13 +220,18 @@ class Coverage(object): self.omit = prep_patterns(self.config.omit) self.include = prep_patterns(self.config.include) + concurrency = self.config.concurrency + if concurrency == "multiprocessing": + patch_multiprocessing() + concurrency = None + self.collector = Collector( should_trace=self._should_trace, check_include=self._check_include_omit_etc, timid=self.config.timid, branch=self.config.branch, warn=self._warn, - concurrency=self.config.concurrency, + concurrency=concurrency, ) # Suffixes are a bit tricky. We want to use the data suffix only when @@ -544,6 +550,8 @@ class Coverage(object): def _warn(self, msg): """Use `msg` as a warning.""" self._warnings.append(msg) + if self.debug.should("pid"): + msg = "[%d] %s" % (os.getpid(), msg) sys.stderr.write("Coverage.py warning: %s\n" % msg) def use_cache(self, usecache): diff --git a/coverage/monkey.py b/coverage/monkey.py new file mode 100644 index 00000000..42f185ea --- /dev/null +++ b/coverage/monkey.py @@ -0,0 +1,45 @@ +"""Monkey-patching to make coverage work right in some cases.""" + +import multiprocessing +import multiprocessing.process +import sys + +# An attribute that will be set on modules to indicate that they have been +# monkey-patched. +MARKER = "_coverage$patched" + + +def patch_multiprocessing(): + """Monkey-patch the multiprocessing module. + + This enables coverage measurement of processes started by multiprocessing. + This is wildly experimental! + + """ + if hasattr(multiprocessing, MARKER): + return + + if sys.version_info >= (3, 4): + klass = multiprocessing.process.BaseProcess + else: + klass = multiprocessing.Process + + original_bootstrap = klass._bootstrap + + class ProcessWithCoverage(klass): + def _bootstrap(self): + from coverage import Coverage + cov = Coverage(data_suffix=True) + cov.start() + try: + return original_bootstrap(self) + finally: + cov.stop() + cov.save() + + if sys.version_info >= (3, 4): + klass._bootstrap = ProcessWithCoverage._bootstrap + else: + multiprocessing.Process = ProcessWithCoverage + + setattr(multiprocessing, MARKER, 1) |