Skip to content

Commit

Permalink
Respect ignore configuration options when --recursive=y. (#6528)
Browse files Browse the repository at this point in the history
* Ignore specified files/directories in recursive mode

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Co-authored-by: Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  • Loading branch information
3 people committed May 13, 2022
1 parent 43c2122 commit b86c394
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 9 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Expand Up @@ -21,6 +21,10 @@ What's New in Pylint 2.13.9?
Release date: TBA


* Respect ignore configuration options with ``--recursive=y``.

Closes #6471

* Fix false positives for ``no-name-in-module`` and ``import-error`` for ``numpy.distutils`` and ``pydantic``.

Closes #6497
Expand Down
20 changes: 16 additions & 4 deletions pylint/lint/expand_modules.py
Expand Up @@ -43,6 +43,20 @@ def _is_in_ignore_list_re(element: str, ignore_list_re: List[Pattern]) -> bool:
return any(file_pattern.match(element) for file_pattern in ignore_list_re)


def _is_ignored_file(
element: str,
ignore_list: list[str],
ignore_list_re: list[Pattern[str]],
ignore_list_paths_re: list[Pattern[str]],
) -> bool:
basename = os.path.basename(element)
return (
basename in ignore_list
or _is_in_ignore_list_re(basename, ignore_list_re)
or _is_in_ignore_list_re(element, ignore_list_paths_re)
)


def expand_modules(
files_or_modules: List[str],
ignore_list: List[str],
Expand All @@ -58,10 +72,8 @@ def expand_modules(

for something in files_or_modules:
basename = os.path.basename(something)
if (
basename in ignore_list
or _is_in_ignore_list_re(os.path.basename(something), ignore_list_re)
or _is_in_ignore_list_re(something, ignore_list_paths_re)
if _is_ignored_file(
something, ignore_list, ignore_list_re, ignore_list_paths_re
):
continue
module_path = get_python_path(something)
Expand Down
17 changes: 13 additions & 4 deletions pylint/lint/pylinter.py
Expand Up @@ -40,7 +40,7 @@
MSG_TYPES_LONG,
MSG_TYPES_STATUS,
)
from pylint.lint.expand_modules import expand_modules
from pylint.lint.expand_modules import _is_ignored_file, expand_modules
from pylint.lint.parallel import check_parallel
from pylint.lint.report_functions import (
report_messages_by_module_stats,
Expand Down Expand Up @@ -1013,9 +1013,8 @@ def initialize(self):
if not msg.may_be_emitted():
self._msgs_state[msg.msgid] = False

@staticmethod
def _discover_files(files_or_modules: Sequence[str]) -> Iterator[str]:
"""Discover python modules and packages in subdirectory.
def _discover_files(self, files_or_modules: Sequence[str]) -> Iterator[str]:
"""Discover python modules and packages in sub-directory.
Returns iterator of paths to discovered modules and packages.
"""
Expand All @@ -1028,6 +1027,16 @@ def _discover_files(files_or_modules: Sequence[str]) -> Iterator[str]:
if any(root.startswith(s) for s in skip_subtrees):
# Skip subtree of already discovered package.
continue

if _is_ignored_file(
root,
self.config.ignore,
self.config.ignore_patterns,
self.config.ignore_paths,
):
skip_subtrees.append(root)
continue

if "__init__.py" in files:
skip_subtrees.append(root)
yield root
Expand Down
64 changes: 64 additions & 0 deletions tests/lint/unittest_lint.py
Expand Up @@ -868,3 +868,67 @@ def test_by_module_statement_value(initialized_linter: PyLinter) -> None:
# Check that the by_module "statement" is equal to the global "statement"
# computed for that module
assert module_stats["statement"] == linter2.stats.statement


@pytest.mark.parametrize(
"ignore_parameter,ignore_parameter_value",
[
("--ignore", "failing.py"),
("--ignore", "ignored_subdirectory"),
("--ignore-patterns", "failing.*"),
("--ignore-patterns", "ignored_*"),
("--ignore-paths", ".*directory/ignored.*"),
("--ignore-paths", ".*ignored.*/failing.*"),
],
)
def test_recursive_ignore(ignore_parameter, ignore_parameter_value) -> None:
run = Run(
[
"--recursive",
"y",
ignore_parameter,
ignore_parameter_value,
join(REGRTEST_DATA_DIR, "directory"),
],
exit=False,
)

linted_files = run.linter._iterate_file_descrs(
tuple(run.linter._discover_files([join(REGRTEST_DATA_DIR, "directory")]))
)
linted_file_paths = [file_item.filepath for file_item in linted_files]

ignored_file = os.path.abspath(
join(REGRTEST_DATA_DIR, "directory", "ignored_subdirectory", "failing.py")
)
assert ignored_file not in linted_file_paths

for regrtest_data_module in (
("directory", "subdirectory", "subsubdirectory", "module.py"),
("directory", "subdirectory", "module.py"),
("directory", "package", "module.py"),
("directory", "package", "subpackage", "module.py"),
):
module = os.path.abspath(join(REGRTEST_DATA_DIR, *regrtest_data_module))
assert module in linted_file_paths


def test_import_sibling_module_from_namespace(initialized_linter: PyLinter) -> None:
"""If the parent directory above `namespace` is on sys.path, ensure that
modules under `namespace` can import each other without raising `import-error`."""
linter = initialized_linter
with tempdir() as tmpdir:
create_files(["namespace/submodule1.py", "namespace/submodule2.py"])
second_path = Path("namespace/submodule2.py")
with open(second_path, "w", encoding="utf-8") as f:
f.write(
"""\"\"\"This module imports submodule1.\"\"\"
import submodule1
print(submodule1)
"""
)
os.chdir("namespace")
# Add the parent directory to sys.path
with fix_import_path([tmpdir]):
linter.check(["submodule2.py"])
assert not linter.stats.by_msg
@@ -0,0 +1 @@
import re
76 changes: 75 additions & 1 deletion tests/test_self.py
Expand Up @@ -1276,17 +1276,91 @@ def test_max_inferred_for_complicated_class_hierarchy() -> None:
assert not ex.value.code % 2

def test_regression_recursive(self):
"""Tests if error is raised when linter is executed over directory not using --recursive=y"""
self._test_output(
[join(HERE, "regrtest_data", "directory", "subdirectory"), "--recursive=n"],
expected_output="No such file or directory",
)

def test_recursive(self):
"""Tests if running linter over directory using --recursive=y"""
self._runtest(
[join(HERE, "regrtest_data", "directory", "subdirectory"), "--recursive=y"],
code=0,
)

def test_ignore_recursive(self):
"""Tests recursive run of linter ignoring directory using --ignore parameter.
Ignored directory contains files yielding lint errors. If directory is not ignored
test would fail due these errors.
"""
self._runtest(
[
join(HERE, "regrtest_data", "directory"),
"--recursive=y",
"--ignore=ignored_subdirectory",
],
code=0,
)

self._runtest(
[
join(HERE, "regrtest_data", "directory"),
"--recursive=y",
"--ignore=failing.py",
],
code=0,
)

def test_ignore_pattern_recursive(self):
"""Tests recursive run of linter ignoring directory using --ignore-parameter parameter.
Ignored directory contains files yielding lint errors. If directory is not ignored
test would fail due these errors.
"""
self._runtest(
[
join(HERE, "regrtest_data", "directory"),
"--recursive=y",
"--ignore-pattern=ignored_.*",
],
code=0,
)

self._runtest(
[
join(HERE, "regrtest_data", "directory"),
"--recursive=y",
"--ignore-pattern=failing.*",
],
code=0,
)

def test_ignore_path_recursive(self):
"""Tests recursive run of linter ignoring directory using --ignore-path parameter.
Ignored directory contains files yielding lint errors. If directory is not ignored
test would fail due these errors.
"""
self._runtest(
[
join(HERE, "regrtest_data", "directory"),
"--recursive=y",
"--ignore-path=.*ignored.*",
],
code=0,
)

self._runtest(
[
join(HERE, "regrtest_data", "directory"),
"--recursive=y",
"--ignore-path=.*failing.*",
],
code=0,
)

def test_recursive_current_dir(self):
with _test_sys_path():
# pytest is including directory HERE/regrtest_data to sys.path which causes
Expand All @@ -1297,7 +1371,7 @@ def test_recursive_current_dir(self):
if not os.path.basename(path) == "regrtest_data"
]
with _test_cwd():
os.chdir(join(HERE, "regrtest_data", "directory"))
os.chdir(join(HERE, "regrtest_data", "directory", "subdirectory"))
self._runtest(
[".", "--recursive=y"],
code=0,
Expand Down

0 comments on commit b86c394

Please sign in to comment.