diff options
author | Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com> | 2021-09-21 13:56:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-21 13:56:58 +0200 |
commit | 1e675abcc2aa931421d7ce300908e734a93fd790 (patch) | |
tree | 9588163cd0c52fe1ffb18f0383d18cf4e0cc6287 | |
parent | 585c6ca9016613a5c7138ad8cd704a91b363d537 (diff) | |
download | pylint-git-1e675abcc2aa931421d7ce300908e734a93fd790.tar.gz |
Type `add_message` and add `MessageLocationTuple` (#5050)
* Type `add_message` and add `MessageLocationTuple`
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
-rw-r--r-- | pylint/checkers/base_checker.py | 18 | ||||
-rw-r--r-- | pylint/message/message.py | 41 | ||||
-rw-r--r-- | pylint/message/message_handler_mix_in.py | 46 | ||||
-rw-r--r-- | pylint/testutils/unittest_linter.py | 15 | ||||
-rw-r--r-- | pylint/typing.py | 11 | ||||
-rw-r--r-- | tests/message/unittest_message.py | 49 | ||||
-rw-r--r-- | tests/testutils/test_output_line.py | 2 |
7 files changed, 131 insertions, 51 deletions
diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py index e690e71d7..16a782f74 100644 --- a/pylint/checkers/base_checker.py +++ b/pylint/checkers/base_checker.py @@ -18,12 +18,14 @@ # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE import functools from inspect import cleandoc -from typing import Any +from typing import Any, Optional + +from astroid import nodes from pylint.config import OptionsProviderMixIn from pylint.constants import _MSG_ORDER, WarningScope from pylint.exceptions import InvalidMessageError -from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements +from pylint.interfaces import Confidence, IRawChecker, ITokenChecker, implements from pylint.message.message_definition import MessageDefinition from pylint.typing import CheckerStats from pylint.utils import get_rst_section, get_rst_title @@ -109,10 +111,14 @@ class BaseChecker(OptionsProviderMixIn): return result def add_message( - self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None - ): - if not confidence: - confidence = UNDEFINED + self, + msgid: str, + line: Optional[int] = None, + node: Optional[nodes.NodeNG] = None, + args: Any = None, + confidence: Optional[Confidence] = None, + col_offset: Optional[int] = None, + ) -> None: self.linter.add_message(msgid, line, node, args, confidence, col_offset) def check_consistency(self): diff --git a/pylint/message/message.py b/pylint/message/message.py index df0e7079e..178079223 100644 --- a/pylint/message/message.py +++ b/pylint/message/message.py @@ -3,8 +3,12 @@ import collections +from typing import Optional, Tuple, Union, overload +from warnings import warn from pylint.constants import MSG_TYPES +from pylint.interfaces import Confidence +from pylint.typing import MessageLocationTuple _MsgBase = collections.namedtuple( "_MsgBase", @@ -28,7 +32,42 @@ _MsgBase = collections.namedtuple( class Message(_MsgBase): """This class represent a message to be issued by the reporters""" - def __new__(cls, msg_id, symbol, location, msg, confidence): + @overload + def __new__( + cls, + msg_id: str, + symbol: str, + location: MessageLocationTuple, + msg: str, + confidence: Optional[Confidence], + ) -> "Message": + ... + + @overload + def __new__( + cls, + msg_id: str, + symbol: str, + location: Tuple[str, str, str, str, int, int], + msg: str, + confidence: Optional[Confidence], + ) -> "Message": + # Remove for pylint 3.0 + ... + + def __new__( + cls, + msg_id: str, + symbol: str, + location: Union[Tuple[str, str, str, str, int, int], MessageLocationTuple], + msg: str, + confidence: Optional[Confidence], + ) -> "Message": + if not isinstance(location, MessageLocationTuple): + warn( + "In pylint 3.0, Messages will only accept a MessageLocationTuple as location parameter", + DeprecationWarning, + ) return _MsgBase.__new__( cls, msg_id, diff --git a/pylint/message/message_handler_mix_in.py b/pylint/message/message_handler_mix_in.py index 6d24131a7..02e193cfa 100644 --- a/pylint/message/message_handler_mix_in.py +++ b/pylint/message/message_handler_mix_in.py @@ -3,7 +3,9 @@ import sys from io import TextIOWrapper -from typing import List, TextIO, Tuple, Union +from typing import TYPE_CHECKING, Any, List, Optional, TextIO, Tuple, Union + +from astroid import nodes from pylint.constants import ( _SCOPE_EXEMPT, @@ -21,10 +23,14 @@ from pylint.exceptions import ( NoLineSuppliedError, UnknownMessageError, ) -from pylint.interfaces import UNDEFINED +from pylint.interfaces import UNDEFINED, Confidence from pylint.message.message import Message from pylint.utils import get_module_and_frameid, get_rst_section, get_rst_title +if TYPE_CHECKING: + from pylint.lint.pylinter import PyLinter + from pylint.message import MessageDefinition + class MessagesHandlerMixIn: """A mix-in class containing all the messages related methods for the main lint class.""" @@ -226,9 +232,15 @@ class MessagesHandlerMixIn: return self._msgs_state.get(msgid, fallback) return self._msgs_state.get(msgid, True) - def add_message( - self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None - ): + def add_message( # type: ignore # MessagesHandlerMixIn is always mixed with PyLinter + self: "PyLinter", + msgid: str, + line: Optional[int] = None, + node: Optional[nodes.NodeNG] = None, + args: Any = None, + confidence: Optional[Confidence] = None, + col_offset: Optional[int] = None, + ) -> None: """Adds a message given by ID or name. If provided, the message string is expanded using args. @@ -267,14 +279,20 @@ class MessagesHandlerMixIn: f"Message {message_definition.msgid} must provide Node, got None" ) - def add_one_message( - self, message_definition, line, node, args, confidence, col_offset - ): + def add_one_message( # type: ignore # MessagesHandlerMixIn is always mixed with PyLinter + self: "PyLinter", + message_definition: "MessageDefinition", + line: Optional[int], + node: Optional[nodes.NodeNG], + args: Any, + confidence: Optional[Confidence], + col_offset: Optional[int], + ) -> None: self.check_message_definition(message_definition, line, node) if line is None and node is not None: line = node.fromlineno if col_offset is None and hasattr(node, "col_offset"): - col_offset = node.col_offset + col_offset = node.col_offset # type: ignore # should this message be displayed if not self.is_message_enabled(message_definition.msgid, line, confidence): @@ -303,13 +321,13 @@ class MessagesHandlerMixIn: "by_module": {self.current_name: {msg_cat: 0}}, "by_msg": {}, } - self.stats[msg_cat] += 1 - self.stats["by_module"][self.current_name][msg_cat] += 1 + self.stats[msg_cat] += 1 # type: ignore + self.stats["by_module"][self.current_name][msg_cat] += 1 # type: ignore try: - self.stats["by_msg"][message_definition.symbol] += 1 + self.stats["by_msg"][message_definition.symbol] += 1 # type: ignore except KeyError: - self.stats["by_msg"][message_definition.symbol] = 1 - # expand message ? + self.stats["by_msg"][message_definition.symbol] = 1 # type: ignore + # Interpolate arguments into message string msg = message_definition.msg if args: msg %= args diff --git a/pylint/testutils/unittest_linter.py b/pylint/testutils/unittest_linter.py index b60190644..c6c187abb 100644 --- a/pylint/testutils/unittest_linter.py +++ b/pylint/testutils/unittest_linter.py @@ -1,6 +1,11 @@ # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +from typing import Any, Optional + +from astroid import nodes + +from pylint.interfaces import Confidence from pylint.testutils.global_test_linter import linter from pylint.testutils.output_line import MessageTest from pylint.typing import CheckerStats @@ -22,8 +27,14 @@ class UnittestLinter: self._messages = [] def add_message( - self, msg_id, line=None, node=None, args=None, confidence=None, col_offset=None - ): + self, + msg_id: str, + line: Optional[int] = None, + node: Optional[nodes.NodeNG] = None, + args: Any = None, + confidence: Optional[Confidence] = None, + col_offset: Optional[int] = None, + ) -> None: # Do not test col_offset for now since changing Message breaks everything self._messages.append(MessageTest(msg_id, line, node, args, confidence)) diff --git a/pylint/typing.py b/pylint/typing.py index 4c12710de..b0171d0d8 100644 --- a/pylint/typing.py +++ b/pylint/typing.py @@ -50,3 +50,14 @@ class ErrorDescriptionDict(TypedDict): CheckerStats = Dict[ str, Union[int, "Counter[str]", List, Dict[str, Union[int, str, Dict[str, int]]]] ] + + +class MessageLocationTuple(NamedTuple): + """Tuple with information about the location of a to-be-displayed message""" + + abspath: str + path: str + module: str + obj: str + line: int + column: int diff --git a/tests/message/unittest_message.py b/tests/message/unittest_message.py index 6f8b4aa01..321453150 100644 --- a/tests/message/unittest_message.py +++ b/tests/message/unittest_message.py @@ -1,29 +1,24 @@ # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE -from typing import Dict, ValuesView +from typing import ValuesView +from pylint.interfaces import HIGH from pylint.message import Message from pylint.message.message_definition import MessageDefinition +from pylint.typing import MessageLocationTuple def test_new_message(message_definitions: ValuesView[MessageDefinition]) -> None: def build_message( - message_definition: MessageDefinition, location_value: Dict[str, str] + message_definition: MessageDefinition, location_value: MessageLocationTuple ) -> Message: return Message( symbol=message_definition.symbol, msg_id=message_definition.msgid, - location=[ - location_value["abspath"], - location_value["path"], - location_value["module"], - location_value["obj"], - location_value["line"], - location_value["column"], - ], + location=location_value, msg=message_definition.msg, - confidence="high", + confidence=HIGH, ) template = "{path}:{line}:{column}: {msg_id}: {msg} ({symbol})" @@ -32,22 +27,22 @@ def test_new_message(message_definitions: ValuesView[MessageDefinition]) -> None e1234_message_definition = message_definition if message_definition.msgid == "W1234": w1234_message_definition = message_definition - e1234_location_values = { - "abspath": "1", - "path": "2", - "module": "3", - "obj": "4", - "line": "5", - "column": "6", - } - w1234_location_values = { - "abspath": "7", - "path": "8", - "module": "9", - "obj": "10", - "line": "11", - "column": "12", - } + e1234_location_values = MessageLocationTuple( + abspath="1", + path="2", + module="3", + obj="4", + line=5, + column=6, + ) + w1234_location_values = MessageLocationTuple( + abspath="7", + path="8", + module="9", + obj="10", + line=11, + column=12, + ) expected = ( "2:5:6: E1234: Duplicate keyword argument %r in %s call (duplicate-keyword-arg)" ) diff --git a/tests/testutils/test_output_line.py b/tests/testutils/test_output_line.py index cd8bc7e9b..e4afa9e33 100644 --- a/tests/testutils/test_output_line.py +++ b/tests/testutils/test_output_line.py @@ -19,7 +19,7 @@ def message() -> Callable: return Message( symbol="missing-docstring", msg_id="C0123", - location=[ + location=[ # type: ignore "abspath", "path", "module", |