summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2021-04-11 10:19:02 -0400
committerNed Batchelder <ned@nedbatchelder.com>2021-04-11 13:37:04 -0400
commit5c2f614e01d35271f7907d85050115071cf24e87 (patch)
treeefab88c677cb4b220313927248c5ac696a471334
parentc19658365310d76c1c64befea178d87637475811 (diff)
downloadpython-coveragepy-git-nedbat/bin-exclude.tar.gz
fix: don't measure third-party scriptsnedbat/bin-exclude
This finishes the last bit of #905 Also includes tighter logging of the reason for not tracing modules.
-rw-r--r--coverage/inorout.py36
-rw-r--r--tests/helpers.py2
-rw-r--r--tests/test_process.py155
3 files changed, 134 insertions, 59 deletions
diff --git a/coverage/inorout.py b/coverage/inorout.py
index 9861dac6..93dbef0e 100644
--- a/coverage/inorout.py
+++ b/coverage/inorout.py
@@ -177,7 +177,7 @@ def add_third_party_paths(paths):
better_scheme = "pypy_posix" if scheme == "pypy" else scheme
if os.name in better_scheme.split("_"):
config_paths = sysconfig.get_paths(scheme)
- for path_name in ["platlib", "purelib"]:
+ for path_name in ["platlib", "purelib", "scripts"]:
paths.add(config_paths[path_name])
@@ -265,9 +265,6 @@ class InOrOut(object):
against.append("modules {!r}".format(self.source_pkgs_match))
debug("Source matching against " + " and ".join(against))
else:
- if self.cover_paths:
- self.cover_match = TreeMatcher(self.cover_paths, "coverage")
- debug("Coverage code matching: {!r}".format(self.cover_match))
if self.pylib_paths:
self.pylib_match = TreeMatcher(self.pylib_paths, "pylib")
debug("Python stdlib matching: {!r}".format(self.pylib_match))
@@ -277,9 +274,12 @@ class InOrOut(object):
if self.omit:
self.omit_match = FnmatchMatcher(self.omit, "omit")
debug("Omit matching: {!r}".format(self.omit_match))
- if self.third_paths:
- self.third_match = TreeMatcher(self.third_paths, "third")
- debug("Third-party lib matching: {!r}".format(self.third_match))
+
+ self.cover_match = TreeMatcher(self.cover_paths, "coverage")
+ debug("Coverage code matching: {!r}".format(self.cover_match))
+
+ self.third_match = TreeMatcher(self.third_paths, "third")
+ debug("Third-party lib matching: {!r}".format(self.third_match))
# Check if the source we want to measure has been installed as a
# third-party package.
@@ -429,27 +429,29 @@ class InOrOut(object):
ok = True
if not ok:
return extra + "falls outside the --source spec"
+ if self.cover_match.match(filename):
+ return "inside --source, but is part of coverage.py"
if not self.source_in_third:
if self.third_match.match(filename):
- return "inside --source, but in third-party"
+ return "inside --source, but is third-party"
elif self.include_match:
if not self.include_match.match(filename):
return "falls outside the --include trees"
else:
+ # We exclude the coverage.py code itself, since a little of it
+ # will be measured otherwise.
+ if self.cover_match.match(filename):
+ return "is part of coverage.py"
+
# If we aren't supposed to trace installed code, then check if this
# is near the Python standard library and skip it if so.
if self.pylib_match and self.pylib_match.match(filename):
return "is in the stdlib"
# Exclude anything in the third-party installation areas.
- if self.third_match and self.third_match.match(filename):
+ if self.third_match.match(filename):
return "is a third-party module"
- # We exclude the coverage.py code itself, since a little of it
- # will be measured otherwise.
- if self.cover_match and self.cover_match.match(filename):
- return "is part of coverage.py"
-
# Check the file against the omit pattern.
if self.omit_match and self.omit_match.match(filename):
return "is inside an --omit pattern"
@@ -485,6 +487,12 @@ class InOrOut(object):
msg = "Already imported a file that will be measured: {}".format(filename)
self.warn(msg, slug="already-imported")
warned.add(filename)
+ elif self.debug and self.debug.should('trace'):
+ self.debug.write(
+ "Didn't trace already imported file {!r}: {}".format(
+ disp.original_filename, disp.reason
+ )
+ )
def warn_unimported_source(self):
"""Warn about source packages that were of interest, but never traced."""
diff --git a/tests/helpers.py b/tests/helpers.py
index 262d9301..daed3d1a 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -240,7 +240,7 @@ def change_dir(new_dir):
"""
old_dir = os.getcwd()
- os.chdir(new_dir)
+ os.chdir(str(new_dir))
try:
yield
finally:
diff --git a/tests/test_process.py b/tests/test_process.py
index b310b770..ef3bbedc 100644
--- a/tests/test_process.py
+++ b/tests/test_process.py
@@ -1643,30 +1643,33 @@ class ProcessStartupWithSourceTest(ProcessCoverageMixin, CoverageTest):
self.assert_pth_and_source_work_together('', 'pkg', 'sub')
-def run_in_venv(args):
- """Run python with `args` in the "venv" virtualenv.
+def run_in_venv(cmd):
+ r"""Run `cmd` in the virtualenv at `venv`.
+
+ The first word of the command will be adjusted to run it from the
+ venv/bin or venv\Scripts directory.
Returns the text output of the command.
"""
+ words = cmd.split()
if env.WINDOWS:
- cmd = r".\venv\Scripts\python.exe "
+ words[0] = r"{}\Scripts\{}.exe".format("venv", words[0])
else:
- cmd = "./venv/bin/python "
- cmd += args
- status, output = run_command(cmd)
- print(output)
+ words[0] = "{}/bin/{}".format("venv", words[0])
+ status, output = run_command(" ".join(words))
assert status == 0
return output
-@pytest.fixture(scope="session", name="venv_factory")
-def venv_factory_fixture(tmp_path_factory):
- """Produce a function which can copy a venv template to a new directory.
+@pytest.fixture(scope="session", name="venv_world")
+def venv_world_fixture(tmp_path_factory):
+ """Create a virtualenv with a few test packages for VirtualenvTest to use.
- The function accepts one argument, the directory to use for the venv.
+ Returns the directory containing the "venv" virtualenv.
"""
- tmpdir = tmp_path_factory.mktemp("venv_template")
- with change_dir(str(tmpdir)):
+
+ venv_world = tmp_path_factory.mktemp("venv_world")
+ with change_dir(venv_world):
# Create a virtualenv.
run_command("python -m virtualenv venv")
@@ -1686,55 +1689,119 @@ def venv_factory_fixture(tmp_path_factory):
""")
# Install the third-party packages.
- run_in_venv("-m pip install --no-index ./third_pkg")
+ run_in_venv("python -m pip install --no-index ./third_pkg")
+ shutil.rmtree("third_pkg")
# Install coverage.
coverage_src = nice_file(TESTS_DIR, "..")
- run_in_venv("-m pip install --no-index {}".format(coverage_src))
+ run_in_venv("python -m pip install --no-index {}".format(coverage_src))
- def factory(dst):
- """The venv factory function.
+ return venv_world
- Copies the venv template to `dst`.
- """
- shutil.copytree(str(tmpdir / "venv"), dst, symlinks=(not env.WINDOWS))
- return factory
+@pytest.fixture(params=[
+ "coverage",
+ "python -m coverage",
+], name="coverage_command")
+def coverage_command_fixture(request):
+ """Parametrized fixture to use multiple forms of "coverage" command."""
+ return request.param
class VirtualenvTest(CoverageTest):
"""Tests of virtualenv considerations."""
- def setup_test(self):
- self.make_file("myproduct.py", """\
- import third
- print(third.third(11))
- """)
- self.del_environ("COVERAGE_TESTING") # To avoid needing contracts installed.
- super(VirtualenvTest, self).setup_test()
-
- def test_third_party_venv_isnt_measured(self, venv_factory):
- venv_factory("venv")
- out = run_in_venv("-m coverage run --source=. myproduct.py")
+ @pytest.fixture(autouse=True)
+ def in_venv_world_fixture(self, venv_world):
+ """For running tests inside venv_world, and cleaning up made files."""
+ with change_dir(venv_world):
+ self.make_file("myproduct.py", """\
+ import colorsys
+ import third
+ print(third.third(11))
+ print(sum(colorsys.rgb_to_hls(1, 0, 0)))
+ """)
+ self.expected_stdout = "33\n1.5\n" # pylint: disable=attribute-defined-outside-init
+
+ self.del_environ("COVERAGE_TESTING") # To avoid needing contracts installed.
+ self.set_environ("COVERAGE_DEBUG_FILE", "debug_out.txt")
+ self.set_environ("COVERAGE_DEBUG", "trace")
+
+ yield
+
+ for fname in os.listdir("."):
+ if fname != "venv":
+ os.remove(fname)
+
+ def get_trace_output(self):
+ """Get the debug output of coverage.py"""
+ with open("debug_out.txt") as f:
+ return f.read()
+
+ def test_third_party_venv_isnt_measured(self, coverage_command):
+ 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 == "33\n"
- out = run_in_venv("-m coverage report")
+ assert out == self.expected_stdout
+
+ # Check that our tracing was accurate. Files are mentioned because
+ # --source refers to a file.
+ debug_out = self.get_trace_output()
+ assert re_lines(
+ debug_out,
+ r"^Not tracing .*\bexecfile.py': inside --source, but is part of coverage.py"
+ )
+ assert re_lines(debug_out, r"^Tracing .*\bmyproduct.py")
+ assert re_lines(
+ debug_out,
+ r"^Not tracing .*\bcolorsys.py': falls outside the --source spec"
+ )
+
+ out = run_in_venv("python -m coverage report")
assert "myproduct.py" in out
assert "third" not in out
+ assert "coverage" not in out
+ assert "colorsys" not in out
+
+ def test_us_in_venv_isnt_measured(self, coverage_command):
+ out = run_in_venv(coverage_command + " run --source=third myproduct.py")
+ assert out == self.expected_stdout
+
+ # Check that our tracing was accurate. Modules are mentioned because
+ # --source refers to a module.
+ debug_out = self.get_trace_output()
+ assert re_lines(
+ debug_out,
+ r"^Not tracing .*\bexecfile.py': " +
+ "module 'coverage.execfile' falls outside the --source spec"
+ )
+ print(re_lines(debug_out, "myproduct"))
+ assert re_lines(
+ debug_out,
+ r"^Not tracing .*\bmyproduct.py': module u?'myproduct' falls outside the --source spec"
+ )
+ assert re_lines(
+ debug_out,
+ r"^Not tracing .*\bcolorsys.py': module u?'colorsys' falls outside the --source spec"
+ )
- def test_us_in_venv_is_measured(self, venv_factory):
- venv_factory("venv")
- out = run_in_venv("-m coverage run --source=third myproduct.py")
- assert out == "33\n"
- out = run_in_venv("-m coverage report")
+ out = run_in_venv("python -m coverage report")
assert "myproduct.py" not in out
assert "third" in out
+ assert "coverage" not in out
+ assert "colorsys" not in out
+
+ def test_venv_isnt_measured(self, coverage_command):
+ out = run_in_venv(coverage_command + " run myproduct.py")
+ assert out == self.expected_stdout
+
+ debug_out = self.get_trace_output()
+ assert re_lines(debug_out, r"^Not tracing .*\bexecfile.py': is part of coverage.py")
+ assert re_lines(debug_out, r"^Tracing .*\bmyproduct.py")
+ assert re_lines(debug_out, r"^Not tracing .*\bcolorsys.py': is in the stdlib")
- def test_venv_isnt_measured(self, venv_factory):
- venv_factory("venv")
- out = run_in_venv("-m coverage run myproduct.py")
- assert out == "33\n"
- out = run_in_venv("-m coverage report")
+ out = run_in_venv("python -m coverage report")
assert "myproduct.py" in out
assert "third" not in out
+ assert "coverage" not in out
+ assert "colorsys" not in out