27
27
from typing import Optional
28
28
29
29
from prospector import encoding
30
+ from prospector .blender import BLEND_COMBOS
30
31
from prospector .exceptions import FatalProspectorException
31
32
from prospector .message import Message
33
+ from prospector .tools .base import ToolBase
32
34
33
35
_FLAKE8_IGNORE_FILE = re .compile (r"flake8[:=]\s*noqa" , re .IGNORECASE )
34
36
_PEP8_IGNORE_LINE = re .compile (r"#\s*noqa(\s*#.*)?$" , re .IGNORECASE )
@@ -51,6 +53,17 @@ def __init__(
51
53
def __str__ (self ) -> str :
52
54
return self .code if self .source is None else f"{ self .source } .{ self .code } "
53
55
56
+ def __repr__ (self ) -> str :
57
+ return f"<{ type (self ).__name__ } { self } >"
58
+
59
+ def __eq__ (self , value : object ) -> bool :
60
+ if not isinstance (value , Ignore ):
61
+ return False
62
+ return self .code == value .code and self .source == value .source
63
+
64
+ def __hash__ (self ) -> int :
65
+ return hash ((self .source , self .code ))
66
+
54
67
55
68
def get_noqa_suppressions (file_contents : list [str ]) -> tuple [bool , set [int ], dict [int , set [Ignore ]]]:
56
69
"""
@@ -81,15 +94,6 @@ def get_noqa_suppressions(file_contents: list[str]) -> tuple[bool, set[int], dic
81
94
return ignore_whole_file , ignore_lines , messages_to_ignore
82
95
83
96
84
- _PYLINT_EQUIVALENTS = {
85
- # TODO: blending has this info already?
86
- "unused-import" : (
87
- ("pyflakes" , "FL0001" ),
88
- ("frosted" , "E101" ),
89
- )
90
- }
91
-
92
-
93
97
def _parse_pylint_informational (
94
98
messages : list [Message ],
95
99
) -> tuple [set [Optional [Path ]], dict [Optional [Path ], dict [int , list [str ]]]]:
@@ -113,17 +117,43 @@ def _parse_pylint_informational(
113
117
return ignore_files , ignore_messages
114
118
115
119
120
+ def _process_tool_ignores (
121
+ tools_ignore : dict [Path , dict [int , set [Ignore ]]],
122
+ blend_combos_dict : dict [Ignore , set [Ignore ]],
123
+ messages_to_ignore : dict [Optional [Path ], dict [int , set [Ignore ]]],
124
+ ) -> None :
125
+ for path , lines_ignore in tools_ignore .items ():
126
+ for line , ignores in lines_ignore .items ():
127
+ for ignore in ignores :
128
+ if ignore in blend_combos_dict :
129
+ messages_to_ignore [path ][line ].update (blend_combos_dict [ignore ])
130
+
131
+
116
132
def get_suppressions (
117
- filepaths : list [Path ], messages : list [Message ]
133
+ filepaths : list [Path ],
134
+ messages : list [Message ],
135
+ tools : Optional [dict [str , ToolBase ]] = None ,
136
+ blending : bool = False ,
137
+ blend_combos : Optional [list [list [tuple [str , str ]]]] = None ,
118
138
) -> tuple [set [Optional [Path ]], dict [Path , set [int ]], dict [Optional [Path ], dict [int , set [Ignore ]]]]:
119
139
"""
120
140
Given every message which was emitted by the tools, and the
121
141
list of files to inspect, create a list of files to ignore,
122
142
and a map of filepath -> line-number -> codes to ignore
123
143
"""
144
+ tools = tools or {}
145
+ blend_combos = blend_combos or BLEND_COMBOS
146
+ blend_combos_dict : dict [Ignore , set [Ignore ]] = {}
147
+ if blending :
148
+ for combo in blend_combos :
149
+ ignore_combos = {Ignore (tool , code ) for tool , code in combo }
150
+ for ignore in ignore_combos :
151
+ blend_combos_dict [ignore ] = ignore_combos
152
+
124
153
paths_to_ignore : set [Optional [Path ]] = set ()
125
154
lines_to_ignore : dict [Path , set [int ]] = defaultdict (set )
126
155
messages_to_ignore : dict [Optional [Path ], dict [int , set [Ignore ]]] = defaultdict (lambda : defaultdict (set ))
156
+ tools_ignore : dict [Path , dict [int , set [Ignore ]]] = defaultdict (lambda : defaultdict (set ))
127
157
128
158
# First deal with 'noqa' style messages
129
159
for filepath in filepaths :
@@ -141,6 +171,17 @@ def get_suppressions(
141
171
for line , codes_ignore in file_messages_to_ignore .items ():
142
172
messages_to_ignore [filepath ][line ] |= codes_ignore
143
173
174
+ if blending :
175
+ for line_number , line_content in enumerate (file_contents ):
176
+ for tool_name , tool in tools .items ():
177
+ tool_ignores = tool .get_ignored_codes (line_content )
178
+ for tool_ignore in tool_ignores :
179
+ tools_ignore [filepath ][line_number + 1 ].add (Ignore (tool_name , tool_ignore ))
180
+
181
+ # Ignore the blending messages
182
+ if blending :
183
+ _process_tool_ignores (tools_ignore , blend_combos_dict , messages_to_ignore )
184
+
144
185
# Now figure out which messages were suppressed by pylint
145
186
pylint_ignore_files , pylint_ignore_messages = _parse_pylint_informational (messages )
146
187
paths_to_ignore |= pylint_ignore_files
@@ -149,9 +190,5 @@ def get_suppressions(
149
190
for code in codes :
150
191
ignore = Ignore ("pylint" , code )
151
192
messages_to_ignore [pylint_filepath ][line_number ].add (ignore )
152
- if code in _PYLINT_EQUIVALENTS :
153
- for ignore_source , ignore_code in _PYLINT_EQUIVALENTS [code ]:
154
- ignore = Ignore (ignore_source , ignore_code )
155
- messages_to_ignore [pylint_filepath ][line_number ].add (ignore )
156
193
157
194
return paths_to_ignore , lines_to_ignore , messages_to_ignore
0 commit comments