Skip to content

Commit 7140b98

Browse files
committed
fix: force patched subprocesses to write data to the original directory. #2025
1 parent d968323 commit 7140b98

File tree

4 files changed

+51
-7
lines changed

4 files changed

+51
-7
lines changed

CHANGES.rst

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,15 @@ upgrading your version of coverage.py.
2323
Unreleased
2424
----------
2525

26-
- Fix: ``patch = subprocess`` didn't fully account for subprocesses spawning
27-
yet more subprocesses simultaneously, and some coveage could be missed. This
28-
is now fixed, closing `issue 2024`_.
26+
- Fixes for ``patch = subprocess``:
27+
28+
- It didn't fully account for subprocesses spawning yet more subprocesses
29+
simultaneously, and some coverage could be missed. This is now fixed,
30+
closing `issue 2024`_.
31+
32+
- If subprocesses were created in other directories, their data files were
33+
stranded there and not combined into the totals, as described in `issue
34+
2025`_. This is now fixed.
2935

3036
- Fix: really close all SQLite databases, even in-memory ones. Closes `issue
3137
2017`_.
@@ -37,6 +43,7 @@ Unreleased
3743
.. _issue 2017: https://github.com/nedbat/coveragepy/issues/2017
3844
.. _pull 2018: https://github.com/nedbat/coveragepy/pull/2018
3945
.. _issue 2024: https://github.com/nedbat/coveragepy/issues/2024
46+
.. _issue 2025: https://github.com/nedbat/coveragepy/issues/2025
4047

4148

4249
.. start-releases

coverage/control.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1448,7 +1448,10 @@ def process_startup() -> Coverage | None:
14481448
# started coverage.py in this process. Nothing to do.
14491449
return None
14501450

1451-
cov = Coverage(config_file=cps)
1451+
cov = Coverage(
1452+
config_file=cps,
1453+
data_file=os.getenv("COVERAGE_PROCESS_DATAFILE") or DEFAULT_DATAFILE,
1454+
)
14521455
process_startup.coverage = cov # type: ignore[attr-defined]
14531456
cov._warn_no_data = False
14541457
cov._warn_unimported_source = False

coverage/patch.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def coverage_execv_patch(*args: Any, **kwargs: Any) -> Any:
8383
atexit.register(pth_file.unlink, missing_ok=True)
8484
assert config.config_file is not None
8585
os.environ["COVERAGE_PROCESS_START"] = config.config_file
86+
os.environ["COVERAGE_PROCESS_DATAFILE"] = os.path.abspath(config.data_file)
8687

8788
else:
8889
raise ConfigError(f"Unknown patch {patch!r}")

tests/test_process.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,9 +1325,8 @@ def test_removing_directory_with_error(self) -> None:
13251325
class ProcessStartupTest(CoverageTest):
13261326
"""Test that we can measure coverage in subprocesses."""
13271327

1328-
def setUp(self) -> None:
1329-
super().setUp()
1330-
1328+
def make_main_and_sub(self) -> None:
1329+
"""Create main.py and sub.py."""
13311330
# Main will run sub.py
13321331
self.make_file("main.py", """\
13331332
import os, os.path, sys
@@ -1342,6 +1341,7 @@ def setUp(self) -> None:
13421341
""")
13431342

13441343
def test_patch_subprocess(self) -> None:
1344+
self.make_main_and_sub()
13451345
self.make_file(".coveragerc", """\
13461346
[run]
13471347
patch = subprocess
@@ -1358,6 +1358,7 @@ def test_subprocess_with_pth_files(self, _create_pth_file: None) -> None:
13581358
# An existing data file should not be read when a subprocess gets
13591359
# measured automatically. Create the data file here with bogus data in
13601360
# it.
1361+
self.make_main_and_sub()
13611362
data = coverage.CoverageData(".mycovdata")
13621363
data.add_lines({os.path.abspath('sub.py'): range(100)})
13631364
data.write()
@@ -1380,6 +1381,7 @@ def test_subprocess_with_pth_files(self, _create_pth_file: None) -> None:
13801381

13811382
def test_subprocess_with_pth_files_and_parallel(self, _create_pth_file: None) -> None:
13821383
# https://github.com/nedbat/coveragepy/issues/492
1384+
self.make_main_and_sub()
13831385
self.make_file("coverage.ini", """\
13841386
[run]
13851387
parallel = true
@@ -1407,6 +1409,37 @@ def test_subprocess_with_pth_files_and_parallel(self, _create_pth_file: None) ->
14071409
)
14081410
assert len(data_files) == 1, msg
14091411

1412+
def test_subprocess_in_directories(self) -> None:
1413+
# Bug 2025: patch=subprocess didn't find data files from subdirectory
1414+
# subprocesses.
1415+
self.make_file("main.py", """\
1416+
import subprocess
1417+
import sys
1418+
print(subprocess.check_output(
1419+
[sys.executable, "subproc.py"],
1420+
cwd="subdir",
1421+
encoding="utf-8",
1422+
))
1423+
""")
1424+
self.make_file("subdir/subproc.py", """\
1425+
with open("readme.txt", encoding="utf-8") as f:
1426+
print(f.read(), end="")
1427+
""")
1428+
self.make_file(".coveragerc", """\
1429+
[run]
1430+
patch = subprocess
1431+
data_file = .covdata
1432+
""")
1433+
self.make_file("subdir/readme.txt", "hello")
1434+
out = self.run_command("coverage run main.py")
1435+
assert out == "hello\n"
1436+
self.run_command("coverage combine")
1437+
data = coverage.CoverageData(".covdata")
1438+
data.read()
1439+
print(line_counts(data))
1440+
assert line_counts(data)["main.py"] == 6
1441+
assert line_counts(data)["subproc.py"] == 2
1442+
14101443

14111444
@pytest.mark.skipif(env.WINDOWS, reason="patch=execv isn't supported on Windows")
14121445
@pytest.mark.xdist_group(name="needs_pth")

0 commit comments

Comments
 (0)