Skip to content

Commit 1e675ab

Browse files
DanielNoordPierre-Sassoulascdce8p
authored
Type add_message and add MessageLocationTuple (#5050)
* Type `add_message` and add `MessageLocationTuple` Co-authored-by: Pierre Sassoulas <[email protected]> Co-authored-by: Marc Mueller <[email protected]>
1 parent 585c6ca commit 1e675ab

File tree

7 files changed

+131
-51
lines changed

7 files changed

+131
-51
lines changed

pylint/checkers/base_checker.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
1919
import functools
2020
from inspect import cleandoc
21-
from typing import Any
21+
from typing import Any, Optional
22+
23+
from astroid import nodes
2224

2325
from pylint.config import OptionsProviderMixIn
2426
from pylint.constants import _MSG_ORDER, WarningScope
2527
from pylint.exceptions import InvalidMessageError
26-
from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements
28+
from pylint.interfaces import Confidence, IRawChecker, ITokenChecker, implements
2729
from pylint.message.message_definition import MessageDefinition
2830
from pylint.typing import CheckerStats
2931
from pylint.utils import get_rst_section, get_rst_title
@@ -109,10 +111,14 @@ def get_full_documentation(self, msgs, options, reports, doc=None, module=None):
109111
return result
110112

111113
def add_message(
112-
self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None
113-
):
114-
if not confidence:
115-
confidence = UNDEFINED
114+
self,
115+
msgid: str,
116+
line: Optional[int] = None,
117+
node: Optional[nodes.NodeNG] = None,
118+
args: Any = None,
119+
confidence: Optional[Confidence] = None,
120+
col_offset: Optional[int] = None,
121+
) -> None:
116122
self.linter.add_message(msgid, line, node, args, confidence, col_offset)
117123

118124
def check_consistency(self):

pylint/message/message.py

+40-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33

44

55
import collections
6+
from typing import Optional, Tuple, Union, overload
7+
from warnings import warn
68

79
from pylint.constants import MSG_TYPES
10+
from pylint.interfaces import Confidence
11+
from pylint.typing import MessageLocationTuple
812

913
_MsgBase = collections.namedtuple(
1014
"_MsgBase",
@@ -28,7 +32,42 @@
2832
class Message(_MsgBase):
2933
"""This class represent a message to be issued by the reporters"""
3034

31-
def __new__(cls, msg_id, symbol, location, msg, confidence):
35+
@overload
36+
def __new__(
37+
cls,
38+
msg_id: str,
39+
symbol: str,
40+
location: MessageLocationTuple,
41+
msg: str,
42+
confidence: Optional[Confidence],
43+
) -> "Message":
44+
...
45+
46+
@overload
47+
def __new__(
48+
cls,
49+
msg_id: str,
50+
symbol: str,
51+
location: Tuple[str, str, str, str, int, int],
52+
msg: str,
53+
confidence: Optional[Confidence],
54+
) -> "Message":
55+
# Remove for pylint 3.0
56+
...
57+
58+
def __new__(
59+
cls,
60+
msg_id: str,
61+
symbol: str,
62+
location: Union[Tuple[str, str, str, str, int, int], MessageLocationTuple],
63+
msg: str,
64+
confidence: Optional[Confidence],
65+
) -> "Message":
66+
if not isinstance(location, MessageLocationTuple):
67+
warn(
68+
"In pylint 3.0, Messages will only accept a MessageLocationTuple as location parameter",
69+
DeprecationWarning,
70+
)
3271
return _MsgBase.__new__(
3372
cls,
3473
msg_id,

pylint/message/message_handler_mix_in.py

+32-14
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
import sys
55
from io import TextIOWrapper
6-
from typing import List, TextIO, Tuple, Union
6+
from typing import TYPE_CHECKING, Any, List, Optional, TextIO, Tuple, Union
7+
8+
from astroid import nodes
79

810
from pylint.constants import (
911
_SCOPE_EXEMPT,
@@ -21,10 +23,14 @@
2123
NoLineSuppliedError,
2224
UnknownMessageError,
2325
)
24-
from pylint.interfaces import UNDEFINED
26+
from pylint.interfaces import UNDEFINED, Confidence
2527
from pylint.message.message import Message
2628
from pylint.utils import get_module_and_frameid, get_rst_section, get_rst_title
2729

30+
if TYPE_CHECKING:
31+
from pylint.lint.pylinter import PyLinter
32+
from pylint.message import MessageDefinition
33+
2834

2935
class MessagesHandlerMixIn:
3036
"""A mix-in class containing all the messages related methods for the main lint class."""
@@ -226,9 +232,15 @@ def is_one_message_enabled(self, msgid, line):
226232
return self._msgs_state.get(msgid, fallback)
227233
return self._msgs_state.get(msgid, True)
228234

229-
def add_message(
230-
self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None
231-
):
235+
def add_message( # type: ignore # MessagesHandlerMixIn is always mixed with PyLinter
236+
self: "PyLinter",
237+
msgid: str,
238+
line: Optional[int] = None,
239+
node: Optional[nodes.NodeNG] = None,
240+
args: Any = None,
241+
confidence: Optional[Confidence] = None,
242+
col_offset: Optional[int] = None,
243+
) -> None:
232244
"""Adds a message given by ID or name.
233245
234246
If provided, the message string is expanded using args.
@@ -267,14 +279,20 @@ def check_message_definition(message_definition, line, node):
267279
f"Message {message_definition.msgid} must provide Node, got None"
268280
)
269281

270-
def add_one_message(
271-
self, message_definition, line, node, args, confidence, col_offset
272-
):
282+
def add_one_message( # type: ignore # MessagesHandlerMixIn is always mixed with PyLinter
283+
self: "PyLinter",
284+
message_definition: "MessageDefinition",
285+
line: Optional[int],
286+
node: Optional[nodes.NodeNG],
287+
args: Any,
288+
confidence: Optional[Confidence],
289+
col_offset: Optional[int],
290+
) -> None:
273291
self.check_message_definition(message_definition, line, node)
274292
if line is None and node is not None:
275293
line = node.fromlineno
276294
if col_offset is None and hasattr(node, "col_offset"):
277-
col_offset = node.col_offset
295+
col_offset = node.col_offset # type: ignore
278296

279297
# should this message be displayed
280298
if not self.is_message_enabled(message_definition.msgid, line, confidence):
@@ -303,13 +321,13 @@ def add_one_message(
303321
"by_module": {self.current_name: {msg_cat: 0}},
304322
"by_msg": {},
305323
}
306-
self.stats[msg_cat] += 1
307-
self.stats["by_module"][self.current_name][msg_cat] += 1
324+
self.stats[msg_cat] += 1 # type: ignore
325+
self.stats["by_module"][self.current_name][msg_cat] += 1 # type: ignore
308326
try:
309-
self.stats["by_msg"][message_definition.symbol] += 1
327+
self.stats["by_msg"][message_definition.symbol] += 1 # type: ignore
310328
except KeyError:
311-
self.stats["by_msg"][message_definition.symbol] = 1
312-
# expand message ?
329+
self.stats["by_msg"][message_definition.symbol] = 1 # type: ignore
330+
# Interpolate arguments into message string
313331
msg = message_definition.msg
314332
if args:
315333
msg %= args

pylint/testutils/unittest_linter.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
33

4+
from typing import Any, Optional
5+
6+
from astroid import nodes
7+
8+
from pylint.interfaces import Confidence
49
from pylint.testutils.global_test_linter import linter
510
from pylint.testutils.output_line import MessageTest
611
from pylint.typing import CheckerStats
@@ -22,8 +27,14 @@ def release_messages(self):
2227
self._messages = []
2328

2429
def add_message(
25-
self, msg_id, line=None, node=None, args=None, confidence=None, col_offset=None
26-
):
30+
self,
31+
msg_id: str,
32+
line: Optional[int] = None,
33+
node: Optional[nodes.NodeNG] = None,
34+
args: Any = None,
35+
confidence: Optional[Confidence] = None,
36+
col_offset: Optional[int] = None,
37+
) -> None:
2738
# Do not test col_offset for now since changing Message breaks everything
2839
self._messages.append(MessageTest(msg_id, line, node, args, confidence))
2940

pylint/typing.py

+11
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,14 @@ class ErrorDescriptionDict(TypedDict):
5050
CheckerStats = Dict[
5151
str, Union[int, "Counter[str]", List, Dict[str, Union[int, str, Dict[str, int]]]]
5252
]
53+
54+
55+
class MessageLocationTuple(NamedTuple):
56+
"""Tuple with information about the location of a to-be-displayed message"""
57+
58+
abspath: str
59+
path: str
60+
module: str
61+
obj: str
62+
line: int
63+
column: int

tests/message/unittest_message.py

+22-27
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
11
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
33

4-
from typing import Dict, ValuesView
4+
from typing import ValuesView
55

6+
from pylint.interfaces import HIGH
67
from pylint.message import Message
78
from pylint.message.message_definition import MessageDefinition
9+
from pylint.typing import MessageLocationTuple
810

911

1012
def test_new_message(message_definitions: ValuesView[MessageDefinition]) -> None:
1113
def build_message(
12-
message_definition: MessageDefinition, location_value: Dict[str, str]
14+
message_definition: MessageDefinition, location_value: MessageLocationTuple
1315
) -> Message:
1416
return Message(
1517
symbol=message_definition.symbol,
1618
msg_id=message_definition.msgid,
17-
location=[
18-
location_value["abspath"],
19-
location_value["path"],
20-
location_value["module"],
21-
location_value["obj"],
22-
location_value["line"],
23-
location_value["column"],
24-
],
19+
location=location_value,
2520
msg=message_definition.msg,
26-
confidence="high",
21+
confidence=HIGH,
2722
)
2823

2924
template = "{path}:{line}:{column}: {msg_id}: {msg} ({symbol})"
@@ -32,22 +27,22 @@ def build_message(
3227
e1234_message_definition = message_definition
3328
if message_definition.msgid == "W1234":
3429
w1234_message_definition = message_definition
35-
e1234_location_values = {
36-
"abspath": "1",
37-
"path": "2",
38-
"module": "3",
39-
"obj": "4",
40-
"line": "5",
41-
"column": "6",
42-
}
43-
w1234_location_values = {
44-
"abspath": "7",
45-
"path": "8",
46-
"module": "9",
47-
"obj": "10",
48-
"line": "11",
49-
"column": "12",
50-
}
30+
e1234_location_values = MessageLocationTuple(
31+
abspath="1",
32+
path="2",
33+
module="3",
34+
obj="4",
35+
line=5,
36+
column=6,
37+
)
38+
w1234_location_values = MessageLocationTuple(
39+
abspath="7",
40+
path="8",
41+
module="9",
42+
obj="10",
43+
line=11,
44+
column=12,
45+
)
5146
expected = (
5247
"2:5:6: E1234: Duplicate keyword argument %r in %s call (duplicate-keyword-arg)"
5348
)

tests/testutils/test_output_line.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def inner(confidence: Confidence = HIGH) -> Message:
1919
return Message(
2020
symbol="missing-docstring",
2121
msg_id="C0123",
22-
location=[
22+
location=[ # type: ignore
2323
"abspath",
2424
"path",
2525
"module",

0 commit comments

Comments
 (0)