Skip to content

Commit 0d246e5

Browse files
DanielNoordcdce8pPierre-Sassoulas
authored
Add typing to filepath (#4980)
* Change tests for ``filepath`` changes * Add pylint/typing.py and FileItem NamedTuple * Use NamedTuple more efficiently * Fix errors and tests after adding warning * Add deprecation for future API change in Checker Co-authored-by: Marc Mueller <[email protected]> Co-authored-by: Pierre Sassoulas <[email protected]>
1 parent dc0c7e9 commit 0d246e5

20 files changed

+164
-115
lines changed

pylint/config/option_manager_mixin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ def load_configuration_from_config(self, config):
332332
provider = self._all_options[opt]
333333
provider.set_option(opt, opt_value)
334334

335-
def load_command_line_configuration(self, args=None):
335+
def load_command_line_configuration(self, args=None) -> List[str]:
336336
"""Override configuration according to command line parameters
337337
338338
return additional arguments

pylint/lint/expand_modules.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
from astroid import modutils
66

7+
from pylint.typing import ErrorDescriptionDict, ModuleDescriptionDict
8+
79

810
def _modpath_from_file(filename, is_namespace, path=None):
911
def _is_package_cb(path, parts):
@@ -42,12 +44,12 @@ def expand_modules(
4244
ignore_list: List[str],
4345
ignore_list_re: List[Pattern],
4446
ignore_list_paths_re: List[Pattern],
45-
) -> Tuple[List[dict], List[dict]]:
47+
) -> Tuple[List[ModuleDescriptionDict], List[ErrorDescriptionDict]]:
4648
"""take a list of files/modules/packages and return the list of tuple
4749
(file, module name) which have to be actually checked
4850
"""
49-
result = []
50-
errors = []
51+
result: List[ModuleDescriptionDict] = []
52+
errors: List[ErrorDescriptionDict] = []
5153
path = sys.path.copy()
5254

5355
for something in files_or_modules:

pylint/lint/parallel.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33

44
import collections
55
import functools
6-
from typing import TYPE_CHECKING, Dict, List, Union
6+
from typing import TYPE_CHECKING, Any, DefaultDict, Dict, Iterable, List, Tuple, Union
77

88
from pylint import reporters
99
from pylint.lint.utils import _patch_sys_path
1010
from pylint.message import Message
11-
from pylint.typing import CheckerStats
11+
from pylint.typing import CheckerStats, FileItem
1212

1313
if TYPE_CHECKING:
1414
from typing import Counter # typing.Counter added in Python 3.6.1
@@ -67,11 +67,13 @@ def _worker_initialize(linter, arguments=None):
6767
_patch_sys_path(arguments or ())
6868

6969

70-
def _worker_check_single_file(file_item):
71-
name, filepath, modname = file_item
72-
70+
def _worker_check_single_file(
71+
file_item: FileItem,
72+
) -> Tuple[int, Any, str, Any, List[Tuple[Any, ...]], Any, Any, DefaultDict[Any, List]]:
73+
if not _worker_linter:
74+
raise Exception("Worker linter not yet initialised")
7375
_worker_linter.open()
74-
_worker_linter.check_single_file(name, filepath, modname)
76+
_worker_linter.check_single_file_item(file_item)
7577
mapreduce_data = collections.defaultdict(list)
7678
for checker in _worker_linter.get_checkers():
7779
try:
@@ -84,7 +86,7 @@ def _worker_check_single_file(file_item):
8486
return (
8587
id(multiprocessing.current_process()),
8688
_worker_linter.current_name,
87-
filepath,
89+
file_item.filepath,
8890
_worker_linter.file_state.base_name,
8991
msgs,
9092
_worker_linter.stats,
@@ -114,7 +116,7 @@ def _merge_mapreduce_data(linter, all_mapreduce_data):
114116
checker.reduce_map_data(linter, collated_map_reduce_data[checker.name])
115117

116118

117-
def check_parallel(linter, jobs, files, arguments=None):
119+
def check_parallel(linter, jobs, files: Iterable[FileItem], arguments=None):
118120
"""Use the given linter to lint the files with given amount of workers (jobs)
119121
This splits the work filestream-by-filestream. If you need to do work across
120122
multiple files, as in the similarity-checker, then inherit from MapReduceMixin and
@@ -156,7 +158,7 @@ def check_parallel(linter, jobs, files, arguments=None):
156158
linter.set_current_module(module, file_path)
157159
for msg in messages:
158160
msg = Message(*msg)
159-
linter.reporter.handle_message(msg)
161+
linter.reporter.handle_message(msg) # type: ignore # linter.set_reporter() call above makes linter have a reporter attr
160162
all_stats.append(stats)
161163
all_mapreduce_data[worker_idx].append(mapreduce_data)
162164
linter.msg_status |= msg_status

pylint/lint/pylinter.py

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import traceback
1212
import warnings
1313
from io import TextIOWrapper
14+
from typing import Iterable, Iterator, List, Optional, Sequence, Union
1415

1516
import astroid
1617
from astroid import AstroidError, nodes
@@ -31,7 +32,7 @@
3132
)
3233
from pylint.message import MessageDefinitionStore, MessagesHandlerMixIn
3334
from pylint.reporters.ureports import nodes as report_nodes
34-
from pylint.typing import CheckerStats
35+
from pylint.typing import CheckerStats, FileItem, ModuleDescriptionDict
3536
from pylint.utils import ASTWalker, FileState, utils
3637
from pylint.utils.pragma_parser import (
3738
OPTION_PO,
@@ -938,16 +939,20 @@ def initialize(self):
938939
if not msg.may_be_emitted():
939940
self._msgs_state[msg.msgid] = False
940941

941-
def check(self, files_or_modules):
942+
def check(self, files_or_modules: Union[Sequence[str], str]) -> None:
942943
"""main checking entry: check a list of files or modules from their name.
943944
944945
files_or_modules is either a string or list of strings presenting modules to check.
945946
"""
946947
self.initialize()
947-
948948
if not isinstance(files_or_modules, (list, tuple)):
949-
files_or_modules = (files_or_modules,)
950-
949+
# pylint: disable-next=fixme
950+
# TODO: Update typing and docstring for 'files_or_modules' when removing the deprecation
951+
warnings.warn(
952+
"In pylint 3.0, the checkers check function will only accept sequence of string",
953+
DeprecationWarning,
954+
)
955+
files_or_modules = (files_or_modules,) # type: ignore
951956
if self.config.from_stdin:
952957
if len(files_or_modules) != 1:
953958
raise exceptions.InvalidArgsError(
@@ -973,66 +978,65 @@ def check(self, files_or_modules):
973978
files_or_modules,
974979
)
975980

976-
def check_single_file(self, name, filepath, modname):
977-
"""Check single file
981+
def check_single_file(self, name: str, filepath: str, modname: str) -> None:
982+
warnings.warn(
983+
"In pylint 3.0, the checkers check_single_file function will be removed. "
984+
"Use check_single_file_item instead.",
985+
DeprecationWarning,
986+
)
987+
self.check_single_file_item(FileItem(name, filepath, modname))
988+
989+
def check_single_file_item(self, file: FileItem) -> None:
990+
"""Check single file item
978991
979992
The arguments are the same that are documented in _check_files
980993
981994
The initialize() method should be called before calling this method
982995
"""
983996
with self._astroid_module_checker() as check_astroid_module:
984-
self._check_file(
985-
self.get_ast, check_astroid_module, name, filepath, modname
986-
)
987-
988-
def _check_files(self, get_ast, file_descrs):
989-
"""Check all files from file_descrs
990-
991-
The file_descrs should be iterable of tuple (name, filepath, modname)
992-
where
993-
- name: full name of the module
994-
- filepath: path of the file
995-
- modname: module name
996-
"""
997+
self._check_file(self.get_ast, check_astroid_module, file)
998+
999+
def _check_files(
1000+
self,
1001+
get_ast,
1002+
file_descrs: Iterable[FileItem],
1003+
) -> None:
1004+
"""Check all files from file_descrs"""
9971005
with self._astroid_module_checker() as check_astroid_module:
998-
for name, filepath, modname in file_descrs:
1006+
for file in file_descrs:
9991007
try:
1000-
self._check_file(
1001-
get_ast, check_astroid_module, name, filepath, modname
1002-
)
1008+
self._check_file(get_ast, check_astroid_module, file)
10031009
except Exception as ex: # pylint: disable=broad-except
10041010
template_path = prepare_crash_report(
1005-
ex, filepath, self.crash_file_path
1011+
ex, file.filepath, self.crash_file_path
10061012
)
1007-
msg = get_fatal_error_message(filepath, template_path)
1013+
msg = get_fatal_error_message(file.filepath, template_path)
10081014
if isinstance(ex, AstroidError):
10091015
symbol = "astroid-error"
1010-
msg = (filepath, msg)
1016+
self.add_message(symbol, args=(file.filepath, msg))
10111017
else:
10121018
symbol = "fatal"
1013-
self.add_message(symbol, args=msg)
1019+
self.add_message(symbol, args=msg)
10141020

1015-
def _check_file(self, get_ast, check_astroid_module, name, filepath, modname):
1021+
def _check_file(self, get_ast, check_astroid_module, file: FileItem):
10161022
"""Check a file using the passed utility functions (get_ast and check_astroid_module)
10171023
10181024
:param callable get_ast: callable returning AST from defined file taking the following arguments
10191025
- filepath: path to the file to check
10201026
- name: Python module name
10211027
:param callable check_astroid_module: callable checking an AST taking the following arguments
10221028
- ast: AST of the module
1023-
:param str name: full name of the module
1024-
:param str filepath: path to checked file
1025-
:param str modname: name of the checked Python module
1029+
:param FileItem file: data about the file
10261030
"""
1027-
self.set_current_module(name, filepath)
1031+
self.set_current_module(file.name, file.filepath)
10281032
# get the module representation
1029-
ast_node = get_ast(filepath, name)
1033+
ast_node = get_ast(file.filepath, file.name)
10301034
if ast_node is None:
10311035
return
10321036

10331037
self._ignore_file = False
10341038

1035-
self.file_state = FileState(modname)
1039+
self.file_state = FileState(file.modpath)
10361040
# fix the current file (if the source file was not available or
10371041
# if it's actually a c extension)
10381042
self.current_file = ast_node.file # pylint: disable=maybe-no-member
@@ -1045,7 +1049,7 @@ def _check_file(self, get_ast, check_astroid_module, name, filepath, modname):
10451049
self.add_message(msgid, line, None, args)
10461050

10471051
@staticmethod
1048-
def _get_file_descr_from_stdin(filepath):
1052+
def _get_file_descr_from_stdin(filepath: str) -> FileItem:
10491053
"""Return file description (tuple of module name, file path, base name) from given file path
10501054
10511055
This method is used for creating suitable file description for _check_files when the
@@ -1059,19 +1063,19 @@ def _get_file_descr_from_stdin(filepath):
10591063
except ImportError:
10601064
modname = os.path.splitext(os.path.basename(filepath))[0]
10611065

1062-
return (modname, filepath, filepath)
1066+
return FileItem(modname, filepath, filepath)
10631067

1064-
def _iterate_file_descrs(self, files_or_modules):
1068+
def _iterate_file_descrs(self, files_or_modules) -> Iterator[FileItem]:
10651069
"""Return generator yielding file descriptions (tuples of module name, file path, base name)
10661070
10671071
The returned generator yield one item for each Python module that should be linted.
10681072
"""
10691073
for descr in self._expand_files(files_or_modules):
10701074
name, filepath, is_arg = descr["name"], descr["path"], descr["isarg"]
10711075
if self.should_analyze_file(name, filepath, is_argument=is_arg):
1072-
yield (name, filepath, descr["basename"])
1076+
yield FileItem(name, filepath, descr["basename"])
10731077

1074-
def _expand_files(self, modules):
1078+
def _expand_files(self, modules) -> List[ModuleDescriptionDict]:
10751079
"""get modules and errors from a list of modules and handle errors"""
10761080
result, errors = expand_modules(
10771081
modules,
@@ -1088,7 +1092,7 @@ def _expand_files(self, modules):
10881092
self.add_message(key, args=message)
10891093
return result
10901094

1091-
def set_current_module(self, modname, filepath=None):
1095+
def set_current_module(self, modname, filepath: Optional[str] = None):
10921096
"""set the name of the currently analyzed module and
10931097
init statistics for it
10941098
"""
@@ -1097,10 +1101,10 @@ def set_current_module(self, modname, filepath=None):
10971101
self.reporter.on_set_current_module(modname, filepath)
10981102
self.current_name = modname
10991103
self.current_file = filepath or modname
1100-
self.stats["by_module"][modname] = {}
1101-
self.stats["by_module"][modname]["statement"] = 0
1104+
self.stats["by_module"][modname] = {} # type: ignore # Refactor of PyLinter.stats necessary
1105+
self.stats["by_module"][modname]["statement"] = 0 # type: ignore
11021106
for msg_cat in MSG_TYPES.values():
1103-
self.stats["by_module"][modname][msg_cat] = 0
1107+
self.stats["by_module"][modname][msg_cat] = 0 # type: ignore
11041108

11051109
@contextlib.contextmanager
11061110
def _astroid_module_checker(self):

pylint/lint/utils.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
import sys
66
import traceback
77
from datetime import datetime
8-
from pathlib import Path, PosixPath
9-
from typing import Union
8+
from pathlib import Path
109

1110
from pylint.config import PYLINT_HOME
1211
from pylint.lint.expand_modules import get_python_path
@@ -16,9 +15,7 @@ class ArgumentPreprocessingError(Exception):
1615
"""Raised if an error occurs during argument preprocessing."""
1716

1817

19-
def prepare_crash_report(
20-
ex: Exception, filepath: PosixPath, crash_file_path: Union[Path, str]
21-
) -> Path:
18+
def prepare_crash_report(ex: Exception, filepath: str, crash_file_path: str) -> Path:
2219
issue_template_path = (
2320
Path(PYLINT_HOME) / datetime.now().strftime(str(crash_file_path))
2421
).resolve()
@@ -63,7 +60,7 @@ def prepare_crash_report(
6360
return issue_template_path
6461

6562

66-
def get_fatal_error_message(filepath: str, issue_template_path: str) -> str:
63+
def get_fatal_error_message(filepath: str, issue_template_path: Path) -> str:
6764
return (
6865
f"Fatal error while checking '{filepath}'. "
6966
f"Please open an issue in our bug tracker so we address this. "

pylint/reporters/base_reporter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import os
55
import sys
6-
from typing import List
6+
from typing import List, Optional
77

88
from pylint.message import Message
99
from pylint.typing import CheckerStats
@@ -63,7 +63,7 @@ def display_messages(self, layout):
6363

6464
# Event callbacks
6565

66-
def on_set_current_module(self, module, filepath):
66+
def on_set_current_module(self, module: str, filepath: Optional[str]) -> None:
6767
"""Hook called when a module starts to be analysed."""
6868

6969
def on_close(

pylint/reporters/multi_reporter.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
import os
6-
from typing import IO, Any, AnyStr, Callable, List, Optional, Union
6+
from typing import IO, Any, AnyStr, Callable, List, Optional
77

88
from pylint.interfaces import IReporter
99
from pylint.message import Message
@@ -12,7 +12,6 @@
1212
from pylint.typing import CheckerStats
1313

1414
AnyFile = IO[AnyStr]
15-
AnyPath = Union[str, bytes, os.PathLike]
1615
PyLinter = Any
1716

1817

@@ -89,7 +88,7 @@ def display_messages(self, layout: BaseLayout) -> None:
8988
for rep in self._sub_reporters:
9089
rep.display_messages(layout)
9190

92-
def on_set_current_module(self, module: str, filepath: Optional[AnyPath]) -> None:
91+
def on_set_current_module(self, module: str, filepath: Optional[str]) -> None:
9392
"""hook called when a module starts to be analysed"""
9493
for rep in self._sub_reporters:
9594
rep.on_set_current_module(module, filepath)

pylint/reporters/text.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import os
2626
import sys
2727
import warnings
28+
from typing import Optional
2829

2930
from pylint import utils
3031
from pylint.interfaces import IReporter
@@ -136,7 +137,7 @@ def __init__(self, output=None):
136137
self._modules = set()
137138
self._template = self.line_format
138139

139-
def on_set_current_module(self, module, filepath):
140+
def on_set_current_module(self, module: str, filepath: Optional[str]) -> None:
140141
self._template = str(self.linter.config.msg_template or self._template)
141142

142143
def write_message(self, msg):

0 commit comments

Comments
 (0)