Skip to content

Commit 8df920b

Browse files
Move all public methods to _MessageStateHandler (#6545)
Co-authored-by: Pierre Sassoulas <[email protected]>
1 parent 4a1a733 commit 8df920b

File tree

2 files changed

+184
-176
lines changed

2 files changed

+184
-176
lines changed

pylint/lint/message_state_handler.py

Lines changed: 182 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,26 @@
44

55
from __future__ import annotations
66

7+
import sys
78
from typing import TYPE_CHECKING
89

9-
from pylint import exceptions
10-
from pylint.constants import MSG_TYPES, MSG_TYPES_LONG
10+
from pylint import exceptions, interfaces
11+
from pylint.constants import (
12+
MSG_STATE_CONFIDENCE,
13+
MSG_STATE_SCOPE_CONFIG,
14+
MSG_STATE_SCOPE_MODULE,
15+
MSG_TYPES,
16+
MSG_TYPES_LONG,
17+
)
1118
from pylint.message import MessageDefinition
1219
from pylint.typing import ManagedMessage
1320

21+
if sys.version_info >= (3, 8):
22+
from typing import Literal
23+
else:
24+
from typing_extensions import Literal
25+
26+
1427
if TYPE_CHECKING:
1528
from pylint.lint.pylinter import PyLinter
1629

@@ -21,6 +34,15 @@ class _MessageStateHandler:
2134
def __init__(self, linter: PyLinter) -> None:
2235
self.linter = linter
2336
self._msgs_state: dict[str, bool] = {}
37+
self._options_methods = {
38+
"enable": self.enable,
39+
"disable": self.disable,
40+
"disable-next": self.disable_next,
41+
}
42+
self._bw_options_methods = {
43+
"disable-msg": self._options_methods["disable"],
44+
"enable-msg": self._options_methods["enable"],
45+
}
2446

2547
def _set_one_msg_status(
2648
self, scope: str, msg: MessageDefinition, line: int | None, enable: bool
@@ -136,3 +158,161 @@ def _register_by_id_managed_msg(
136158
self.linter.current_name, msgid_or_symbol, symbol, line, is_disabled
137159
)
138160
self.linter._by_id_managed_msgs.append(managed)
161+
162+
def disable(
163+
self,
164+
msgid: str,
165+
scope: str = "package",
166+
line: int | None = None,
167+
ignore_unknown: bool = False,
168+
) -> None:
169+
"""Disable a message for a scope."""
170+
self._set_msg_status(
171+
msgid, enable=False, scope=scope, line=line, ignore_unknown=ignore_unknown
172+
)
173+
self._register_by_id_managed_msg(msgid, line)
174+
175+
def disable_next(
176+
self,
177+
msgid: str,
178+
scope: str = "package",
179+
line: int | None = None,
180+
ignore_unknown: bool = False,
181+
) -> None:
182+
"""Disable a message for the next line."""
183+
if not line:
184+
raise exceptions.NoLineSuppliedError
185+
self._set_msg_status(
186+
msgid,
187+
enable=False,
188+
scope=scope,
189+
line=line + 1,
190+
ignore_unknown=ignore_unknown,
191+
)
192+
self._register_by_id_managed_msg(msgid, line + 1)
193+
194+
def enable(
195+
self,
196+
msgid: str,
197+
scope: str = "package",
198+
line: int | None = None,
199+
ignore_unknown: bool = False,
200+
) -> None:
201+
"""Enable a message for a scope."""
202+
self._set_msg_status(
203+
msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown
204+
)
205+
self._register_by_id_managed_msg(msgid, line, is_disabled=False)
206+
207+
def disable_noerror_messages(self) -> None:
208+
for msgcat, msgids in self.linter.msgs_store._msgs_by_category.items():
209+
# enable only messages with 'error' severity and above ('fatal')
210+
if msgcat in {"E", "F"}:
211+
for msgid in msgids:
212+
self.enable(msgid)
213+
else:
214+
for msgid in msgids:
215+
self.disable(msgid)
216+
217+
def list_messages_enabled(self) -> None:
218+
emittable, non_emittable = self.linter.msgs_store.find_emittable_messages()
219+
enabled: list[str] = []
220+
disabled: list[str] = []
221+
for message in emittable:
222+
if self.is_message_enabled(message.msgid):
223+
enabled.append(f" {message.symbol} ({message.msgid})")
224+
else:
225+
disabled.append(f" {message.symbol} ({message.msgid})")
226+
print("Enabled messages:")
227+
for msg in enabled:
228+
print(msg)
229+
print("\nDisabled messages:")
230+
for msg in disabled:
231+
print(msg)
232+
print("\nNon-emittable messages with current interpreter:")
233+
for msg_def in non_emittable:
234+
print(f" {msg_def.symbol} ({msg_def.msgid})")
235+
print("")
236+
237+
def _get_message_state_scope(
238+
self,
239+
msgid: str,
240+
line: int | None = None,
241+
confidence: interfaces.Confidence | None = None,
242+
) -> Literal[0, 1, 2] | None:
243+
"""Returns the scope at which a message was enabled/disabled."""
244+
if confidence is None:
245+
confidence = interfaces.UNDEFINED
246+
if confidence.name not in self.linter.config.confidence:
247+
return MSG_STATE_CONFIDENCE # type: ignore[return-value] # mypy does not infer Literal correctly
248+
try:
249+
if line in self.linter.file_state._module_msgs_state[msgid]:
250+
return MSG_STATE_SCOPE_MODULE # type: ignore[return-value]
251+
except (KeyError, TypeError):
252+
return MSG_STATE_SCOPE_CONFIG # type: ignore[return-value]
253+
return None
254+
255+
def _is_one_message_enabled(self, msgid: str, line: int | None) -> bool:
256+
"""Checks state of a single message for the current file.
257+
258+
This function can't be cached as it depends on self.file_state which can
259+
change.
260+
"""
261+
if line is None:
262+
return self._msgs_state.get(msgid, True)
263+
try:
264+
return self.linter.file_state._module_msgs_state[msgid][line]
265+
except KeyError:
266+
# Check if the message's line is after the maximum line existing in ast tree.
267+
# This line won't appear in the ast tree and won't be referred in
268+
# self.file_state._module_msgs_state
269+
# This happens for example with a commented line at the end of a module.
270+
max_line_number = self.linter.file_state.get_effective_max_line_number()
271+
if max_line_number and line > max_line_number:
272+
fallback = True
273+
lines = self.linter.file_state._raw_module_msgs_state.get(msgid, {})
274+
275+
# Doesn't consider scopes, as a 'disable' can be in a
276+
# different scope than that of the current line.
277+
closest_lines = reversed(
278+
[
279+
(message_line, enable)
280+
for message_line, enable in lines.items()
281+
if message_line <= line
282+
]
283+
)
284+
_, fallback_iter = next(closest_lines, (None, None))
285+
if fallback_iter is not None:
286+
fallback = fallback_iter
287+
288+
return self._msgs_state.get(msgid, fallback)
289+
return self._msgs_state.get(msgid, True)
290+
291+
def is_message_enabled(
292+
self,
293+
msg_descr: str,
294+
line: int | None = None,
295+
confidence: interfaces.Confidence | None = None,
296+
) -> bool:
297+
"""Return whether this message is enabled for the current file, line and confidence level.
298+
299+
This function can't be cached right now as the line is the line of
300+
the currently analysed file (self.file_state), if it changes, then the
301+
result for the same msg_descr/line might need to change.
302+
303+
:param msg_descr: Either the msgid or the symbol for a MessageDefinition
304+
:param line: The line of the currently analysed file
305+
:param confidence: The confidence of the message
306+
"""
307+
if confidence and confidence.name not in self.linter.config.confidence:
308+
return False
309+
try:
310+
msgids = self.linter.msgs_store.message_id_store.get_active_msgids(
311+
msg_descr
312+
)
313+
except exceptions.UnknownMessageError:
314+
# The linter checks for messages that are not registered
315+
# due to version mismatch, just treat them as message IDs
316+
# for now.
317+
msgids = [msg_descr]
318+
return any(self._is_one_message_enabled(msgid, line) for msgid in msgids)

0 commit comments

Comments
 (0)