4
4
5
5
from __future__ import annotations
6
6
7
+ import sys
7
8
from typing import TYPE_CHECKING
8
9
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
+ )
11
18
from pylint .message import MessageDefinition
12
19
from pylint .typing import ManagedMessage
13
20
21
+ if sys .version_info >= (3 , 8 ):
22
+ from typing import Literal
23
+ else :
24
+ from typing_extensions import Literal
25
+
26
+
14
27
if TYPE_CHECKING :
15
28
from pylint .lint .pylinter import PyLinter
16
29
@@ -21,6 +34,15 @@ class _MessageStateHandler:
21
34
def __init__ (self , linter : PyLinter ) -> None :
22
35
self .linter = linter
23
36
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
+ }
24
46
25
47
def _set_one_msg_status (
26
48
self , scope : str , msg : MessageDefinition , line : int | None , enable : bool
@@ -136,3 +158,161 @@ def _register_by_id_managed_msg(
136
158
self .linter .current_name , msgid_or_symbol , symbol , line , is_disabled
137
159
)
138
160
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