summaryrefslogtreecommitdiff
path: root/pylint
diff options
context:
space:
mode:
Diffstat (limited to 'pylint')
-rw-r--r--pylint/lint/pylinter.py136
1 files changed, 100 insertions, 36 deletions
diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py
index b83efc253..472b3b030 100644
--- a/pylint/lint/pylinter.py
+++ b/pylint/lint/pylinter.py
@@ -14,14 +14,14 @@ import tokenize
import traceback
import warnings
from collections import defaultdict
-from collections.abc import Callable, Iterable, Iterator, Sequence
+from collections.abc import Callable, Iterator, Sequence
from io import TextIOWrapper
from pathlib import Path
from re import Pattern
from typing import Any
import astroid
-from astroid import AstroidError, nodes
+from astroid import nodes
from pylint import checkers, exceptions, interfaces, reporters
from pylint.checkers.base_checker import BaseChecker
@@ -645,33 +645,63 @@ class PyLinter(
"Missing filename required for --from-stdin"
)
+ # TODO: Move the parallel invocation into step 5 of the checking process
+ if not self.config.from_stdin and self.config.jobs > 1:
+ original_sys_path = sys.path[:]
+ check_parallel(
+ self,
+ self.config.jobs,
+ self._iterate_file_descrs(files_or_modules),
+ files_or_modules, # this argument patches sys.path
+ )
+ sys.path = original_sys_path
+ return
+
# 3) Get all FileItems
with fix_import_path(files_or_modules):
if self.config.from_stdin:
fileitems = iter(
(self._get_file_descr_from_stdin(files_or_modules[0]),)
)
+ data: str | None = _read_stdin()
else:
fileitems = self._iterate_file_descrs(files_or_modules)
+ data = None
- if self.config.from_stdin:
+ # The contextmanager also opens all checkers and sets up the PyLinter class
+ with self._astroid_module_checker() as check_astroid_module:
with fix_import_path(files_or_modules):
- self._check_files(
- functools.partial(self.get_ast, data=_read_stdin()),
- fileitems,
+ # 4) Get the AST for each FileItem
+ ast_per_fileitem = self._get_asts(fileitems, data)
+
+ # 5) Lint each ast
+ self._lint_files(ast_per_fileitem, check_astroid_module)
+
+ def _get_asts(
+ self, fileitems: Iterator[FileItem], data: str | None
+ ) -> dict[FileItem, nodes.Module | None]:
+ """Get the AST for all given FileItems."""
+ ast_per_fileitem: dict[FileItem, nodes.Module | None] = {}
+
+ for fileitem in fileitems:
+ self.set_current_module(fileitem.name, fileitem.filepath)
+
+ try:
+ ast_per_fileitem[fileitem] = self.get_ast(
+ fileitem.filepath, fileitem.name, data
)
- elif self.config.jobs == 1:
- with fix_import_path(files_or_modules):
- self._check_files(self.get_ast, fileitems)
- else:
- original_sys_path = sys.path[:]
- check_parallel(
- self,
- self.config.jobs,
- fileitems,
- files_or_modules, # this argument patches sys.path
- )
- sys.path = original_sys_path
+ except astroid.AstroidBuildingError as ex:
+ template_path = prepare_crash_report(
+ ex, fileitem.filepath, self.crash_file_path
+ )
+ msg = get_fatal_error_message(fileitem.filepath, template_path)
+ self.add_message(
+ "astroid-error",
+ args=(fileitem.filepath, msg),
+ confidence=HIGH,
+ )
+
+ return ast_per_fileitem
def check_single_file(self, name: str, filepath: str, modname: str) -> None:
warnings.warn(
@@ -691,27 +721,61 @@ class PyLinter(
with self._astroid_module_checker() as check_astroid_module:
self._check_file(self.get_ast, check_astroid_module, file)
- def _check_files(
+ def _lint_files(
self,
- get_ast: GetAstProtocol,
- file_descrs: Iterable[FileItem],
+ ast_mapping: dict[FileItem, nodes.Module | None],
+ check_astroid_module: Callable[[nodes.Module], bool | None],
) -> None:
- """Check all files from file_descrs."""
- with self._astroid_module_checker() as check_astroid_module:
- for file in file_descrs:
- try:
- self._check_file(get_ast, check_astroid_module, file)
- except Exception as ex: # pylint: disable=broad-except
- template_path = prepare_crash_report(
- ex, file.filepath, self.crash_file_path
+ """Lint all AST modules from a mapping.."""
+ for fileitem, module in ast_mapping.items():
+ if module is None:
+ continue
+ try:
+ self._lint_file(fileitem, module, check_astroid_module)
+ except Exception as ex: # pylint: disable=broad-except
+ template_path = prepare_crash_report(
+ ex, fileitem.filepath, self.crash_file_path
+ )
+ msg = get_fatal_error_message(fileitem.filepath, template_path)
+ if isinstance(ex, astroid.AstroidError):
+ self.add_message(
+ "astroid-error", args=(fileitem.filepath, msg), confidence=HIGH
)
- msg = get_fatal_error_message(file.filepath, template_path)
- if isinstance(ex, AstroidError):
- self.add_message(
- "astroid-error", args=(file.filepath, msg), confidence=HIGH
- )
- else:
- self.add_message("fatal", args=msg, confidence=HIGH)
+ else:
+ self.add_message("fatal", args=msg, confidence=HIGH)
+
+ def _lint_file(
+ self,
+ file: FileItem,
+ module: nodes.Module,
+ check_astroid_module: Callable[[nodes.Module], bool | None],
+ ) -> None:
+ """Lint a file using the passed utility function check_astroid_module).
+
+ :param FileItem file: data about the file
+ :param nodes.Module module: the ast module to lint
+ :param Callable check_astroid_module: callable checking an AST taking the following arguments
+ - ast: AST of the module
+ :raises AstroidError: for any failures stemming from astroid
+ """
+ self.set_current_module(file.name, file.filepath)
+ self._ignore_file = False
+ self.file_state = FileState(file.modpath, self.msgs_store, module)
+ # fix the current file (if the source file was not available or
+ # if it's actually a c extension)
+ self.current_file = module.file
+
+ try:
+ check_astroid_module(module)
+ except Exception as e:
+ raise astroid.AstroidError from e
+
+ # warn about spurious inline messages handling
+ spurious_messages = self.file_state.iter_spurious_suppression_messages(
+ self.msgs_store
+ )
+ for msgid, line, args in spurious_messages:
+ self.add_message(msgid, line, None, args)
def _check_file(
self,