44
55from __future__ import annotations
66
7+ import sys
78from 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+ )
1118from pylint .message import MessageDefinition
1219from 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+
1427if 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 ("\n Disabled messages:" )
230+ for msg in disabled :
231+ print (msg )
232+ print ("\n Non-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