diff options
| author | Ned Batchelder <ned@nedbatchelder.com> | 2022-01-23 07:15:08 -0500 |
|---|---|---|
| committer | Ned Batchelder <ned@nedbatchelder.com> | 2022-01-23 13:44:58 -0500 |
| commit | dd575eec0fa173bcb6c680d2f1c822a28280d011 (patch) | |
| tree | 44992b89c286d9c8bb86478f2e70c03616e54a11 /coverage | |
| parent | 2e8c1910cad1ba23726e62e03c4ae1608f3fb26e (diff) | |
| download | python-coveragepy-git-dd575eec0fa173bcb6c680d2f1c822a28280d011.tar.gz | |
fix: save data on SIGTERM #1307
This covers multiprocessing.Process.terminate(), and maybe other cases also.
Diffstat (limited to 'coverage')
| -rw-r--r-- | coverage/control.py | 19 | ||||
| -rw-r--r-- | coverage/multiproc.py | 2 |
2 files changed, 18 insertions, 3 deletions
diff --git a/coverage/control.py b/coverage/control.py index bd51ffc5..913d6893 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -9,6 +9,7 @@ import contextlib import os import os.path import platform +import signal import sys import time import warnings @@ -228,6 +229,7 @@ class Coverage: self._exclude_re = None self._debug = None self._file_mapper = None + self._old_sigterm = None # State machine variables: # Have we initialized everything? @@ -526,6 +528,11 @@ class Coverage: self._should_write_debug = True atexit.register(self._atexit) + if not env.WINDOWS: + # The Python docs seem to imply that SIGTERM works uniformly even + # on Windows, but that's not my experience, and this agrees: + # https://stackoverflow.com/questions/35772001/x/35792192#35792192 + self._old_sigterm = signal.signal(signal.SIGTERM, self._on_sigterm) def _init_data(self, suffix): """Create a data file if we don't have one yet.""" @@ -583,15 +590,23 @@ class Coverage: self._collector.stop() self._started = False - def _atexit(self): + def _atexit(self, event="atexit"): """Clean up on process shutdown.""" if self._debug.should("process"): - self._debug.write(f"atexit: pid: {os.getpid()}, instance: {self!r}") + self._debug.write(f"{event}: pid: {os.getpid()}, instance: {self!r}") if self._started: self.stop() if self._auto_save: self.save() + def _on_sigterm(self, signum_unused, frame_unused): + """A handler for signal.SIGTERM.""" + self._atexit("sigterm") + # Statements after here won't be seen by metacov because we just wrote + # the data, and are about to kill the process. + signal.signal(signal.SIGTERM, self._old_sigterm) # pragma: not covered + os.kill(os.getpid(), signal.SIGTERM) # pragma: not covered + def erase(self): """Erase previously collected coverage data. diff --git a/coverage/multiproc.py b/coverage/multiproc.py index 1f9225f3..3a9bd633 100644 --- a/coverage/multiproc.py +++ b/coverage/multiproc.py @@ -27,7 +27,7 @@ class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-m """Wrapper around _bootstrap to start coverage.""" try: from coverage import Coverage # avoid circular import - cov = Coverage(data_suffix=True) + cov = Coverage(data_suffix=True, auto_data=True) cov._warn_preimported_source = False cov.start() debug = cov._debug |
