summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManuel Jacob <me@manueljacob.de>2023-02-21 03:03:55 +0100
committerNed Batchelder <ned@nedbatchelder.com>2023-03-15 05:48:29 -0400
commit4574ecf128ae51c2b950f6c9cb2486b86f5354e7 (patch)
tree80db11484a335c1d8ebc558492c0915a3db4d515
parentdd7959dbf4eede4d2f2254f607113e45071cc8d3 (diff)
downloadpython-coveragepy-git-4574ecf128ae51c2b950f6c9cb2486b86f5354e7.tar.gz
fix: don't measure all third-party packages if source is in third-party location
There is logic to not measure third-party packages inside configured sources. However, when a (i.e. another) configured source was inside a third-party location, this logic was previously disabled completely. This caused a problem if a virtual env is set up inside a configured source directory and a configured source package gets installed inside the virtual env. Previously in this case, coverage was measured for all files in the virtual env for the reason described in the previous paragraph. This commit changes the code to collect all configured source directories inside third-party locations and disable coverage for code in third-party locations only if its not in one of these collected source directories.
-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,
)