Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add combine --keep #1110

Merged
merged 4 commits into from Jan 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion coverage/cmdline.py
Expand Up @@ -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.",
Expand Down Expand Up @@ -215,6 +219,7 @@ def __init__(self, *args, **kwargs):
help=None,
ignore_errors=None,
include=None,
keep=None,
module=None,
omit=None,
contexts=None,
Expand Down Expand Up @@ -333,6 +338,7 @@ def get_prog_name(self):
"combine",
[
Opts.append,
Opts.keep,
] + GLOBAL_ARGS,
usage="[options] <path1> <path2> ... <pathN>",
description=(
Expand Down Expand Up @@ -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=bool(options.keep))
self.coverage.save()
return OK

Expand Down
7 changes: 5 additions & 2 deletions coverage/control.py
Expand Up @@ -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
Expand All @@ -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 files won't be deleted.

.. versionadded:: 4.0
The `data_paths` parameter.

Expand All @@ -694,7 +696,8 @@ 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.
Expand Down
11 changes: 6 additions & 5 deletions coverage/data.py
Expand Up @@ -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
Expand 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.

Expand Down Expand Up @@ -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")
1 change: 1 addition & 0 deletions doc/help/combine.rst
Expand Up @@ -13,6 +13,7 @@
Options:
-a, --append Append coverage data to .coverage, otherwise it starts
clean each time.
--keep Keep combined coverage files, otherwise they are deleted.
--debug=OPTS Debug options, separated by commas. [env: COVERAGE_DEBUG]
-h, --help Get help on this command.
--rcfile=RCFILE Specify configuration file. By default '.coveragerc',
Expand Down
4 changes: 4 additions & 0 deletions doc/python-coverage.1.txt
Expand Up @@ -109,6 +109,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.
Unless --keep is provided the combined coverage files are deleted.

If `PATH` is specified, they are files or directories containing data to
be combined.
Expand All @@ -119,6 +120,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
Expand Down
2 changes: 1 addition & 1 deletion tests/test_api.py
Expand Up @@ -442,7 +442,7 @@ def test_combining_twice(self):

cov2 = coverage.Coverage()
with self.assertRaisesRegex(CoverageException, r"No data to combine"):
cov2.combine(strict=True)
cov2.combine(strict=True, keep=False)

cov3 = coverage.Coverage()
cov3.combine()
Expand Down
10 changes: 5 additions & 5 deletions tests/test_cmdline.py
Expand Up @@ -218,33 +218,33 @@ def test_combine(self):
# coverage combine with args
self.cmd_executes("combine datadir1", """\
cov = Coverage()
cov.combine(["datadir1"], strict=True)
cov.combine(["datadir1"], strict=True, keep=False)
cov.save()
""")
# coverage combine, appending
self.cmd_executes("combine --append datadir1", """\
cov = Coverage()
cov.load()
cov.combine(["datadir1"], strict=True)
cov.combine(["datadir1"], strict=True, keep=False)
cov.save()
""")
# coverage combine without args
self.cmd_executes("combine", """\
cov = Coverage()
cov.combine(None, strict=True)
cov.combine(None, strict=True, keep=False)
cov.save()
""")

def test_combine_doesnt_confuse_options_with_args(self):
# https://github.com/nedbat/coveragepy/issues/385
self.cmd_executes("combine --rcfile cov.ini", """\
cov = Coverage(config_file='cov.ini')
cov.combine(None, strict=True)
cov.combine(None, strict=True, keep=False)
cov.save()
""")
self.cmd_executes("combine --rcfile cov.ini data1 data2/more", """\
cov = Coverage(config_file='cov.ini')
cov.combine(["data1", "data2/more"], strict=True)
cov.combine(["data1", "data2/more"], strict=True, keep=False)
cov.save()
""")

Expand Down
22 changes: 22 additions & 0 deletions tests/test_process.py
Expand Up @@ -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")

# After combining, the .coverage file & the original combined file should still be there.
self.assert_exists(".coverage")
self.assert_file_count(".coverage.*", 2)


def test_append_data(self):
self.make_b_or_c_py()

Expand Down