From 9fa662ca9c1b20bd2781e564d6a78d31111e5bb0 Mon Sep 17 00:00:00 2001 From: Daniel Mouritzen Date: Tue, 4 Apr 2023 05:44:15 +0900 Subject: Handle patterns in ignored-modules when checking for no-name-in-module (#7579) Co-authored-by: Pierre Sassoulas Co-authored-by: Jared Proffitt --- pylint/checkers/imports.py | 22 +++++---------------- pylint/checkers/typecheck.py | 2 +- pylint/checkers/utils.py | 46 ++++++++++++++++++-------------------------- pylint/checkers/variables.py | 5 ++++- 4 files changed, 29 insertions(+), 46 deletions(-) (limited to 'pylint') diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index 9f4b0e379..6cc5d2164 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -23,6 +23,7 @@ from pylint.checkers.utils import ( get_import_name, in_type_checking_block, is_from_fallback_block, + is_module_ignored, is_sys_guard, node_ignores_exception, ) @@ -84,18 +85,6 @@ DEPRECATED_MODULES = { } -def _qualified_names(modname: str | None) -> list[str]: - """Split the names of the given module into subparts. - - For example, - _qualified_names('pylint.checkers.ImportsChecker') - returns - ['pylint', 'pylint.checkers', 'pylint.checkers.ImportsChecker'] - """ - names = modname.split(".") if modname is not None else "" - return [".".join(names[0 : i + 1]) for i in range(len(names))] - - def _get_first_import( node: ImportNode, context: nodes.LocalsDictNodeNG, @@ -153,12 +142,11 @@ def _get_first_import( def _ignore_import_failure( node: ImportNode, - modname: str | None, + modname: str, ignored_modules: Sequence[str], ) -> bool: - for submodule in _qualified_names(modname): - if submodule in ignored_modules: - return True + if is_module_ignored(modname, ignored_modules): + return True # Ignore import failure if part of guarded import block # I.e. `sys.version_info` or `typing.TYPE_CHECKING` @@ -852,7 +840,7 @@ class ImportsChecker(DeprecatedMixin, BaseChecker): return std_imports, external_imports, local_imports def _get_imported_module( - self, importnode: ImportNode, modname: str | None + self, importnode: ImportNode, modname: str ) -> nodes.Module | None: try: return importnode.do_import_module(modname) diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 90604261d..42131f936 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -124,7 +124,7 @@ def _is_owner_ignored( matches any name from the *ignored_classes* or if its qualified name can be found in *ignored_classes*. """ - if is_module_ignored(owner.root(), ignored_modules): + if is_module_ignored(owner.root().qname(), ignored_modules): return True # Match against ignored classes. diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 4b316909e..38321bc11 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -12,7 +12,6 @@ import itertools import numbers import re import string -from collections import deque from collections.abc import Iterable, Iterator from functools import lru_cache, partial from re import Match @@ -2050,37 +2049,30 @@ def is_augmented_assign(node: nodes.Assign) -> tuple[bool, str]: return False, "" +def _qualified_name_parts(qualified_module_name: str) -> list[str]: + """Split the names of the given module into subparts. + + For example, + _qualified_name_parts('pylint.checkers.ImportsChecker') + returns + ['pylint', 'pylint.checkers', 'pylint.checkers.ImportsChecker'] + """ + names = qualified_module_name.split(".") + return [".".join(names[0 : i + 1]) for i in range(len(names))] + + def is_module_ignored( - module: nodes.Module, - ignored_modules: Iterable[str], + qualified_module_name: str, ignored_modules: Iterable[str] ) -> bool: ignored_modules = set(ignored_modules) - module_name = module.name - module_qname = module.qname() - - for ignore in ignored_modules: - # Try to match the module name / fully qualified name directly - if module_qname in ignored_modules or module_name in ignored_modules: + for current_module in _qualified_name_parts(qualified_module_name): + # Try to match the module name directly + if current_module in ignored_modules: return True - - # Try to see if the ignores pattern match against the module name. - if fnmatch.fnmatch(module_qname, ignore): - return True - - # Otherwise, we might have a root module name being ignored, - # and the qualified owner has more levels of depth. - parts = deque(module_name.split(".")) - current_module = "" - - while parts: - part = parts.popleft() - if not current_module: - current_module = part - else: - current_module += f".{part}" - if current_module in ignored_modules: + for ignore in ignored_modules: + # Try to see if the ignores pattern match against the module name. + if fnmatch.fnmatch(current_module, ignore): return True - return False diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 0a24de370..79bdb6a97 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -25,6 +25,7 @@ from astroid.typing import InferenceResult from pylint.checkers import BaseChecker, utils from pylint.checkers.utils import ( in_type_checking_block, + is_module_ignored, is_postponed_evaluation_enabled, is_sys_guard, overridden_method, @@ -2941,7 +2942,9 @@ class VariablesChecker(BaseChecker): if not isinstance(module, nodes.Module): return None except astroid.NotFoundError: - if module.name in self._ignored_modules: + # Unable to import `name` from `module`. Since `name` may itself be a + # module, we first check if it matches the ignored modules. + if is_module_ignored(f"{module.qname()}.{name}", self._ignored_modules): return None self.add_message( "no-name-in-module", args=(name, module.name), node=node -- cgit v1.2.1