summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2021-09-21 13:56:58 +0200
committerGitHub <noreply@github.com>2021-09-21 13:56:58 +0200
commit1e675abcc2aa931421d7ce300908e734a93fd790 (patch)
tree9588163cd0c52fe1ffb18f0383d18cf4e0cc6287
parent585c6ca9016613a5c7138ad8cd704a91b363d537 (diff)
downloadpylint-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.py18
-rw-r--r--pylint/message/message.py41
-rw-r--r--pylint/message/message_handler_mix_in.py46
-rw-r--r--pylint/testutils/unittest_linter.py15
-rw-r--r--pylint/typing.py11
-rw-r--r--tests/message/unittest_message.py49
-rw-r--r--tests/testutils/test_output_line.py2
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",