Add ignored modules to Astroid module deny list (#9504)
diff --git a/.pyenchant_pylint_custom_dict.txt b/.pyenchant_pylint_custom_dict.txt
index fd4fed0..4bcff9c 100644
--- a/.pyenchant_pylint_custom_dict.txt
+++ b/.pyenchant_pylint_custom_dict.txt
@@ -300,6 +300,7 @@
sqlalchemy
src
starargs
+stateful
staticmethod
stderr
stdin
diff --git a/doc/user_guide/configuration/all-options.rst b/doc/user_guide/configuration/all-options.rst
index 94d2c17..cef8e2a 100644
--- a/doc/user_guide/configuration/all-options.rst
+++ b/doc/user_guide/configuration/all-options.rst
@@ -120,7 +120,7 @@
--ignored-modules
"""""""""""""""""
-*List of module names for which member attributes should not be checked (useful for modules/projects where namespaces are manipulated during runtime and thus existing member attributes cannot be deduced by static analysis). It supports qualified module names, as well as Unix pattern matching.*
+*List of module names for which member attributes should not be checked and will not be imported (useful for modules/projects where namespaces are manipulated during runtime and thus existing member attributes cannot be deduced by static analysis). It supports qualified module names, as well as Unix pattern matching.*
**Default:** ``()``
diff --git a/doc/whatsnew/fragments/9442.other b/doc/whatsnew/fragments/9442.other
new file mode 100644
index 0000000..9523aba
--- /dev/null
+++ b/doc/whatsnew/fragments/9442.other
@@ -0,0 +1,5 @@
+Ignored modules are now not checked at all, instead of being checked and then
+ignored. This should speed up the analysis of large codebases which have
+ignored modules.
+
+Closes #9442 (`#9442 <https://github.com/pylint-dev/pylint/issues/9442>`_)
diff --git a/examples/pylintrc b/examples/pylintrc
index d478005..4685415 100644
--- a/examples/pylintrc
+++ b/examples/pylintrc
@@ -59,10 +59,11 @@
# Emacs file locks
ignore-patterns=^\.#
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis). It
-# supports qualified module names, as well as Unix pattern matching.
+# List of module names for which member attributes should not be checked and
+# will not be imported (useful for modules/projects where namespaces are
+# manipulated during runtime and thus existing member attributes cannot be
+# deduced by static analysis). It supports qualified module names, as well
+# as Unix pattern matching.
ignored-modules=
# Python code to execute, usually for sys.path manipulation such as
diff --git a/examples/pyproject.toml b/examples/pyproject.toml
index a8ec9a7..7d9ffe8 100644
--- a/examples/pyproject.toml
+++ b/examples/pyproject.toml
@@ -49,10 +49,11 @@
# file locks
ignore-patterns = ["^\\.#"]
-# List of module names for which member attributes should not be checked (useful
-# for modules/projects where namespaces are manipulated during runtime and thus
-# existing member attributes cannot be deduced by static analysis). It supports
-# qualified module names, as well as Unix pattern matching.
+# List of module names for which member attributes should not be checked and
+# will not be imported (useful for modules/projects where namespaces are
+# manipulated during runtime and thus existing member attributes cannot be
+# deduced by static analysis). It supports qualified module names, as well
+# as Unix pattern matching.
# ignored-modules =
# Python code to execute, usually for sys.path manipulation such as
diff --git a/pylint/lint/base_options.py b/pylint/lint/base_options.py
index 3d5ba5d..cd354c4 100644
--- a/pylint/lint/base_options.py
+++ b/pylint/lint/base_options.py
@@ -384,7 +384,8 @@
"type": "csv",
"metavar": "<module names>",
"help": "List of module names for which member attributes "
- "should not be checked (useful for modules/projects "
+ "should not be checked and will not be imported "
+ "(useful for modules/projects "
"where namespaces are manipulated during runtime and "
"thus existing member attributes cannot be "
"deduced by static analysis). It supports qualified "
diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py
index 3025015..f1aca76 100644
--- a/pylint/lint/pylinter.py
+++ b/pylint/lint/pylinter.py
@@ -1073,6 +1073,7 @@
MANAGER.always_load_extensions = self.config.unsafe_load_any_extension
MANAGER.max_inferable_values = self.config.limit_inference_results
MANAGER.extension_package_whitelist.update(self.config.extension_pkg_allow_list)
+ MANAGER.module_denylist.update(self.config.ignored_modules)
if self.config.extension_pkg_whitelist:
MANAGER.extension_package_whitelist.update(
self.config.extension_pkg_whitelist
diff --git a/pylintrc b/pylintrc
index 434fa23..a943b1c 100644
--- a/pylintrc
+++ b/pylintrc
@@ -342,10 +342,11 @@
# members is set to 'yes'
mixin-class-rgx=.*MixIn
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis). It
-# supports qualified module names, as well as Unix pattern matching.
+# List of module names for which member attributes should not be checked and
+# will not be imported (useful for modules/projects where namespaces are
+# manipulated during runtime and thus existing member attributes cannot be
+# deduced by static analysis). It supports qualified module names, as well
+# as Unix pattern matching.
ignored-modules=
# List of class names for which member attributes should not be checked (useful
diff --git a/tests/lint/test_pylinter.py b/tests/lint/test_pylinter.py
index 1e4f40a..c14d192 100644
--- a/tests/lint/test_pylinter.py
+++ b/tests/lint/test_pylinter.py
@@ -10,7 +10,7 @@
from pytest import CaptureFixture
-from pylint.lint.pylinter import PyLinter
+from pylint.lint.pylinter import MANAGER, PyLinter
from pylint.utils import FileState
@@ -48,3 +48,14 @@
assert len(files) == 1
assert "pylint-crash-20" in str(files[0])
assert any(m.symbol == "astroid-error" for m in linter.reporter.messages)
+
+
+def test_open_pylinter_denied_modules(linter: PyLinter) -> None:
+ """Test PyLinter open() adds ignored modules to Astroid manager deny list."""
+ MANAGER.module_denylist = {"mod1"}
+ try:
+ linter.config.ignored_modules = ["mod2", "mod3"]
+ linter.open()
+ assert MANAGER.module_denylist == {"mod1", "mod2", "mod3"}
+ finally:
+ MANAGER.module_denylist = set()
diff --git a/tests/test_functional.py b/tests/test_functional.py
index ef0a373..df42767 100644
--- a/tests/test_functional.py
+++ b/tests/test_functional.py
@@ -7,13 +7,16 @@
from __future__ import annotations
import sys
+from collections.abc import Iterator
from pathlib import Path
+from typing import TYPE_CHECKING
import pytest
from _pytest.config import Config
from pylint import testutils
from pylint.constants import PY312_PLUS
+from pylint.lint.pylinter import MANAGER
from pylint.testutils import UPDATE_FILE, UPDATE_OPTION
from pylint.testutils.functional import (
FunctionalTestFile,
@@ -22,6 +25,9 @@
)
from pylint.utils import HAS_ISORT_5
+if TYPE_CHECKING:
+ from pylint.lint import PyLinter
+
FUNCTIONAL_DIR = Path(__file__).parent.resolve() / "functional"
@@ -40,6 +46,14 @@
]
+@pytest.fixture
+def revert_stateful_config_changes(linter: PyLinter) -> Iterator[PyLinter]:
+ yield linter
+ # Revert any stateful configuration changes.
+ MANAGER.brain["module_denylist"] = set()
+
+
+@pytest.mark.usefixtures("revert_stateful_config_changes")
@pytest.mark.parametrize("test_file", TESTS, ids=TESTS_NAMES)
def test_functional(test_file: FunctionalTestFile, pytestconfig: Config) -> None:
__tracebackhide__ = True # pylint: disable=unused-variable