diff --git a/prospector/config/__init__.py b/prospector/config/__init__.py index 603c56e2..d275720a 100644 --- a/prospector/config/__init__.py +++ b/prospector/config/__init__.py @@ -53,16 +53,19 @@ def make_exclusion_filter(self) -> Callable[[Path], bool]: ignores, workdir = self.ignores, self.workdir def _filter(path: Path) -> bool: - for ignore in ignores: - # first figure out where the path is, relative to the workdir - # ignore-paths/patterns will usually be relative to a repository - # root or the CWD, but the path passed to prospector may not be - path = path.resolve().absolute() - if is_relative_to(path, workdir): - path = path.relative_to(workdir) - if ignore.match(str(path)): - return True - return False + # first figure out where the path is, relative to the workdir + # ignore-paths/patterns will usually be relative to a repository + # root or the CWD, but the path passed to prospector may not be. + # A symlink is matched under its own name as well as its target, + # since users expect to be able to ignore the link they can see. + candidates = [] + for candidate in (path.absolute(), path.resolve().absolute()): + if is_relative_to(candidate, workdir): + candidate = candidate.relative_to(workdir) + if candidate not in candidates: + candidates.append(candidate) + + return any(ignore.match(str(candidate)) for ignore in ignores for candidate in candidates) return _filter diff --git a/tests/config/test_config.py b/tests/config/test_config.py index e1ae7359..d0fec44d 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -19,6 +19,25 @@ def test_relative_ignores() -> None: assert len(files.python_modules) == 2 +def test_symlinked_ignore_path(tmp_path: Path) -> None: + """ + Tests that 'ignore-paths: ' ignores the symlink itself, + not only the path its target resolves to. + """ + target = tmp_path / "realdir" + target.mkdir() + (target / "module.py").write_text("x = 1\n") + (tmp_path / "link").symlink_to(target, target_is_directory=True) + (tmp_path / "profile_symlink_ignores.yml").write_text("ignore-paths:\n - link\n") + + with patch_execution("-P", "profile_symlink_ignores.yml", set_cwd=tmp_path): + config = ProspectorConfig() + exclusion_filter = config.make_exclusion_filter() + + assert exclusion_filter(tmp_path / "link") + assert exclusion_filter(tmp_path / "link" / "module.py") + + def test_determine_ignores_all_str() -> None: with patch_execution("-P", "prospector-str-ignores", set_cwd=Path(__file__).parent): config = ProspectorConfig()