From eb48bc85682a6884b222a8295a0700921203ff4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Larivi=C3=A8re?= Date: Sat, 30 Jan 2021 00:52:57 -0500 Subject: [PATCH] Add combine --keep Related to https://github.com/nedbat/coveragepy/issues/1108 --- coverage/cmdline.py | 8 +++++++- coverage/control.py | 6 ++++-- coverage/data.py | 11 ++++++----- doc/python-coverage.1.txt | 5 ++++- tests/test_process.py | 22 ++++++++++++++++++++++ 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/coverage/cmdline.py b/coverage/cmdline.py index 9c9ae868a..f337daaac 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -31,6 +31,10 @@ class Opts(object): '-a', '--append', action='store_true', help="Append coverage data to .coverage, otherwise it starts clean each time.", ) + keep = optparse.make_option( + '', '--keep', action='store_true', + help="Keep combined coverage files, otherwise they are deleted.", + ) branch = optparse.make_option( '', '--branch', action='store_true', help="Measure branch coverage in addition to statement coverage.", @@ -215,6 +219,7 @@ def __init__(self, *args, **kwargs): help=None, ignore_errors=None, include=None, + keep=None, module=None, omit=None, contexts=None, @@ -333,6 +338,7 @@ def get_prog_name(self): "combine", [ Opts.append, + Opts.keep, ] + GLOBAL_ARGS, usage="[options] ... ", description=( @@ -585,7 +591,7 @@ def command_line(self, argv): if options.append: self.coverage.load() data_dirs = args or None - self.coverage.combine(data_dirs, strict=True) + self.coverage.combine(data_dirs, strict=True, keep=options.keep) self.coverage.save() return OK diff --git a/coverage/control.py b/coverage/control.py index 8d129bcb5..388d271f1 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -659,7 +659,7 @@ def save(self): data = self.get_data() data.write() - def combine(self, data_paths=None, strict=False): + def combine(self, data_paths=None, strict=False, keep=False): """Combine together a number of similarly-named coverage data files. All coverage data files whose name starts with `data_file` (from the @@ -674,6 +674,8 @@ def combine(self, data_paths=None, strict=False): If `strict` is true, then it is an error to attempt to combine when there are no data files to combine. + If `keep` is true, then combined data file won't be deleted. + .. versionadded:: 4.0 The `data_paths` parameter. @@ -694,7 +696,7 @@ def combine(self, data_paths=None, strict=False): for pattern in paths[1:]: aliases.add(pattern, result) - combine_parallel_data(self._data, aliases=aliases, data_paths=data_paths, strict=strict) + combine_parallel_data(self._data, aliases=aliases, data_paths=data_paths, strict=strict, keep=keep) def get_data(self): """Get the collected data. diff --git a/coverage/data.py b/coverage/data.py index 82bf1d41c..5dd1dfe3f 100644 --- a/coverage/data.py +++ b/coverage/data.py @@ -52,7 +52,7 @@ def add_data_to_hash(data, filename, hasher): hasher.update(data.file_tracer(filename)) -def combine_parallel_data(data, aliases=None, data_paths=None, strict=False): +def combine_parallel_data(data, aliases=None, data_paths=None, strict=False, keep=False): """Combine a number of data files together. Treat `data.filename` as a file prefix, and combine the data from all @@ -68,7 +68,7 @@ def combine_parallel_data(data, aliases=None, data_paths=None, strict=False): If `data_paths` is not provided, then the directory portion of `data.filename` is used as the directory to search for data files. - Every data file found and combined is then deleted from disk. If a file + Unless `keep` is True every data file found and combined is then deleted from disk. If a file cannot be read, a warning will be issued, and the file will not be deleted. @@ -116,9 +116,10 @@ def combine_parallel_data(data, aliases=None, data_paths=None, strict=False): else: data.update(new_data, aliases=aliases) files_combined += 1 - if data._debug.should('dataio'): - data._debug.write("Deleting combined data file %r" % (f,)) - file_be_gone(f) + if not keep: + if data._debug.should('dataio'): + data._debug.write("Deleting combined data file %r" % (f,)) + file_be_gone(f) if strict and not files_combined: raise CoverageException("No usable data files") diff --git a/doc/python-coverage.1.txt b/doc/python-coverage.1.txt index 0bbd44d0a..b1887746e 100644 --- a/doc/python-coverage.1.txt +++ b/doc/python-coverage.1.txt @@ -108,7 +108,7 @@ COMMAND REFERENCE Combine data from multiple coverage files collected with ``run -p``. The combined results are written to a single file representing the - union of the data. + union of the data. Unless --keep is provided the combined coverage files are deleted. If `PATH` is specified, they are files or directories containing data to be combined. @@ -119,6 +119,9 @@ COMMAND REFERENCE Append coverage data to .coverage, otherwise it starts clean each time. + \--keep + Keep combined coverage file. + **debug** `TOPIC` ... Display information about the internals of coverage.py, for diagnosing diff --git a/tests/test_process.py b/tests/test_process.py index e48861568..c729ea420 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -244,6 +244,28 @@ def test_combine_parallel_data_no_append(self): data.read() self.assertEqual(line_counts(data)['b_or_c.py'], 7) + def test_combine_parallel_data_keep(self): + self.make_b_or_c_py() + out = self.run_command("coverage run -p b_or_c.py b") + self.assertEqual(out, 'done\n') + self.assert_doesnt_exist(".coverage") + self.assert_file_count(".coverage.*", 1) + + out = self.run_command("coverage run -p b_or_c.py c") + self.assertEqual(out, 'done\n') + self.assert_doesnt_exist(".coverage") + + # After two -p runs, there should be two .coverage.machine.123 files. + self.assert_file_count(".coverage.*", 2) + + # Combine the parallel coverage data files into .coverage with the keep flag. + self.run_command("coverage combine --keep") + self.assert_exists(".coverage") + + # After combining, there should be the .coverage file and the original combined file should still be there. + self.assert_file_count(".coverage.*", 3) + + def test_append_data(self): self.make_b_or_c_py()