summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst7
-rw-r--r--CONTRIBUTORS.txt1
-rw-r--r--coverage/inorout.py25
-rw-r--r--tests/test_venv.py26
4 files changed, 44 insertions, 15 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 74f2d4bc..b4060746 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -20,6 +20,12 @@ development at the same time, such as 4.5.x and 5.0.
Unreleased
----------
+- Fix: if a virtualenv was created inside a source directory, and a sourced
+ package was installed inside the virtualenv, then all of the third-party
+ packages inside the virtualenv would be measured. This was incorrect, but
+ has now been fixed: only the specified packages will be measured, thanks to
+ `Manuel Jacob <pull 1560_>`_.
+
- Fix: the ``coverage lcov`` command could create a .lcov file with incorrect
LF (lines found) and LH (lines hit) totals. This is now fixed, thanks to
`Ian Moore <pull 1583_>`_.
@@ -28,6 +34,7 @@ Unreleased
duplicate ``<package>`` elements. This is now fixed, thanks to `Benjamin
Parzella <pull 1574_>`_, closing `issue 1573`_.
+.. _pull 1560: https://github.com/nedbat/coveragepy/pull/1560
.. _issue 1573: https://github.com/nedbat/coveragepy/issues/1573
.. _pull 1574: https://github.com/nedbat/coveragepy/pull/1574
.. _pull 1583: https://github.com/nedbat/coveragepy/pull/1583
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index bb69b823..a50138f8 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -107,6 +107,7 @@ Leonardo Pistone
Lex Berezhny
Loïc Dachary
Lorenzo Micò
+Manuel Jacob
Marc Abramowitz
Marc Legendre
Marcelo Trylesinski
diff --git a/coverage/inorout.py b/coverage/inorout.py
index babaa3d8..d0d0ef91 100644
--- a/coverage/inorout.py
+++ b/coverage/inorout.py
@@ -262,7 +262,7 @@ class InOrOut:
# Check if the source we want to measure has been installed as a
# third-party package.
# Is the source inside a third-party area?
- self.source_in_third = False
+ self.source_in_third_paths = set()
with sys_modules_saved():
for pkg in self.source_pkgs:
try:
@@ -274,22 +274,23 @@ class InOrOut:
if modfile:
if self.third_match.match(modfile):
_debug(
- f"Source is in third-party because of source_pkg {pkg!r} at {modfile!r}"
+ f"Source in third-party: source_pkg {pkg!r} at {modfile!r}"
)
- self.source_in_third = True
+ self.source_in_third_paths.add(canonical_path(source_for_file(modfile)))
else:
for pathdir in path:
if self.third_match.match(pathdir):
_debug(
- f"Source is in third-party because of {pkg!r} path directory " +
- f"at {pathdir!r}"
+ f"Source in third-party: {pkg!r} path directory at {pathdir!r}"
)
- self.source_in_third = True
+ self.source_in_third_paths.add(pathdir)
for src in self.source:
if self.third_match.match(src):
- _debug(f"Source is in third-party because of source directory {src!r}")
- self.source_in_third = True
+ _debug(f"Source in third-party: source directory {src!r}")
+ self.source_in_third_paths.add(src)
+ self.source_in_third_match = TreeMatcher(self.source_in_third_paths, "source_in_third")
+ _debug(f"Source in third-party matching: {self.source_in_third_match}")
self.plugins: Plugins
self.disp_class: Type[TFileDisposition] = FileDisposition
@@ -419,9 +420,8 @@ class InOrOut:
ok = True
if not ok:
return extra + "falls outside the --source spec"
- if not self.source_in_third:
- if self.third_match.match(filename):
- return "inside --source, but is third-party"
+ if self.third_match.match(filename) and not self.source_in_third_match.match(filename):
+ return "inside --source, but is third-party"
elif self.include_match:
if not self.include_match.match(filename):
return "falls outside the --include trees"
@@ -576,12 +576,13 @@ class InOrOut:
("coverage_paths", self.cover_paths),
("stdlib_paths", self.pylib_paths),
("third_party_paths", self.third_paths),
+ ("source_in_third_party_paths", self.source_in_third_paths),
]
matcher_names = [
'source_match', 'source_pkgs_match',
'include_match', 'omit_match',
- 'cover_match', 'pylib_match', 'third_match',
+ 'cover_match', 'pylib_match', 'third_match', 'source_in_third_match',
]
for matcher_name in matcher_names:
diff --git a/tests/test_venv.py b/tests/test_venv.py
index de7ebbe1..ae5b303f 100644
--- a/tests/test_venv.py
+++ b/tests/test_venv.py
@@ -198,8 +198,28 @@ class VirtualenvTest(CoverageTest):
with open("debug_out.txt") as f:
return f.read()
- def test_third_party_venv_isnt_measured(self, coverage_command: str) -> None:
- out = run_in_venv(coverage_command + " run --source=. myproduct.py")
+ @pytest.mark.parametrize('install_source_in_venv', [True, False])
+ def test_third_party_venv_isnt_measured(
+ self, coverage_command: str, install_source_in_venv: bool
+ ) -> None:
+ if install_source_in_venv:
+ make_file("setup.py", """\
+ import setuptools
+ setuptools.setup(
+ name="myproduct",
+ py_modules = ["myproduct"],
+ )
+ """)
+ try:
+ run_in_venv("python -m pip install .")
+ finally:
+ shutil.rmtree("build", ignore_errors=True)
+ shutil.rmtree("myproduct.egg-info", ignore_errors=True)
+ # Ensure that coverage doesn't run the non-installed module.
+ os.remove('myproduct.py')
+ out = run_in_venv(coverage_command + " run --source=.,myproduct -m myproduct")
+ else:
+ out = run_in_venv(coverage_command + " run --source=. myproduct.py")
# In particular, this warning doesn't appear:
# Already imported a file that will be measured: .../coverage/__main__.py
assert out == self.expected_stdout
@@ -213,7 +233,7 @@ class VirtualenvTest(CoverageTest):
)
assert re_lines(r"^Tracing .*\bmyproduct.py", debug_out)
assert re_lines(
- r"^Not tracing .*\bcolorsys.py': falls outside the --source spec",
+ r"^Not tracing .*\bcolorsys.py': (module 'colorsys' |)?falls outside the --source spec",
debug_out,
)