10 import queue |
10 import queue |
11 import ast |
11 import ast |
12 import re |
12 import re |
13 import traceback |
13 import traceback |
14 import multiprocessing |
14 import multiprocessing |
15 |
15 import contextlib |
16 |
16 |
17 try: |
17 with contextlib.suppress(ImportError): |
18 from pyflakes.checker import Checker |
18 from pyflakes.checker import Checker |
19 from pyflakes.messages import ImportStarUsed, ImportStarUsage |
19 from pyflakes.messages import ImportStarUsed, ImportStarUsage |
20 except ImportError: |
|
21 pass |
|
22 |
20 |
23 VcsConflictMarkerRegExpList = ( |
21 VcsConflictMarkerRegExpList = ( |
24 re.compile( |
22 re.compile( |
25 r"""^<<<<<<< .*?\|\|\|\|\|\|\| .*?=======.*?>>>>>>> .*?$""", |
23 r"""^<<<<<<< .*?\|\|\|\|\|\|\| .*?=======.*?>>>>>>> .*?$""", |
26 re.MULTILINE | re.DOTALL |
24 re.MULTILINE | re.DOTALL |
58 @return normalized code (string) |
56 @return normalized code (string) |
59 """ |
57 """ |
60 codestring = codestring.replace("\r\n", "\n").replace("\r", "\n") |
58 codestring = codestring.replace("\r\n", "\n").replace("\r", "\n") |
61 |
59 |
62 if codestring and codestring[-1] != '\n': |
60 if codestring and codestring[-1] != '\n': |
63 codestring = codestring + '\n' |
61 codestring += '\n' |
64 |
62 |
65 return codestring |
63 return codestring |
66 |
64 |
67 |
65 |
68 def extractLineFlags(line, startComment="#", endComment="", flagsLine=False): |
66 def extractLineFlags(line, startComment="#", endComment="", flagsLine=False): |
205 ignoreStarImportWarnings=False): |
203 ignoreStarImportWarnings=False): |
206 """ |
204 """ |
207 Function to compile one Python source file to Python bytecode |
205 Function to compile one Python source file to Python bytecode |
208 and to perform a pyflakes check. |
206 and to perform a pyflakes check. |
209 |
207 |
210 @param filename source filename (string) |
208 @param filename source filename |
211 @param codestring string containing the code to compile (string) |
209 @type str |
212 @param checkFlakes flag indicating to do a pyflakes check (boolean) |
210 @param codestring string containing the code to compile |
|
211 @type str |
|
212 @param checkFlakes flag indicating to do a pyflakes check |
|
213 @type bool |
213 @param ignoreStarImportWarnings flag indicating to |
214 @param ignoreStarImportWarnings flag indicating to |
214 ignore 'star import' warnings (boolean) |
215 ignore 'star import' warnings |
|
216 @type bool |
215 @return dictionary with the keys 'error' and 'warnings' which |
217 @return dictionary with the keys 'error' and 'warnings' which |
216 hold a list containing details about the error/ warnings |
218 hold a list containing details about the error/ warnings |
217 (file name, line number, column, codestring (only at syntax |
219 (file name, line number, column, codestring (only at syntax |
218 errors), the message, a list with arguments for the message) |
220 errors), the message, a list with arguments for the message) |
|
221 @rtype dict |
219 """ |
222 """ |
220 import builtins |
223 import builtins |
221 |
224 |
222 try: |
225 try: |
223 codestring = normalizeCode(codestring) |
226 codestring = normalizeCode(codestring) |
276 fn = filename |
279 fn = filename |
277 line = 1 |
280 line = 1 |
278 error = str(detail) |
281 error = str(detail) |
279 return [{'error': (fn, line, 0, "", error)}] |
282 return [{'error': (fn, line, 0, "", error)}] |
280 except Exception as detail: |
283 except Exception as detail: |
281 try: |
284 with contextlib.suppress(Exception): |
282 fn = detail.filename |
285 fn = detail.filename |
283 line = detail.lineno |
286 line = detail.lineno |
284 error = detail.msg |
287 error = detail.msg |
285 return [{'error': (fn, line, 0, "", error)}] |
288 return [{'error': (fn, line, 0, "", error)}] |
286 except Exception: # secok |
|
287 pass |
|
288 |
289 |
289 # pyflakes |
290 # pyflakes |
290 if not checkFlakes: |
291 if not checkFlakes: |
291 return [{}] |
292 return [{}] |
292 |
293 |
294 lines = codestring.splitlines() |
295 lines = codestring.splitlines() |
295 try: |
296 try: |
296 warnings = Checker(module, filename, withDoctest=True) |
297 warnings = Checker(module, filename, withDoctest=True) |
297 warnings.messages.sort(key=lambda a: a.lineno) |
298 warnings.messages.sort(key=lambda a: a.lineno) |
298 for warning in warnings.messages: |
299 for warning in warnings.messages: |
299 if ignoreStarImportWarnings and ( |
300 if ( |
300 isinstance(warning, ImportStarUsed) or |
301 ignoreStarImportWarnings and |
301 isinstance(warning, ImportStarUsage) |
302 isinstance(warning, (ImportStarUsed, ImportStarUsage)) |
302 ): |
303 ): |
303 continue |
304 continue |
304 |
305 |
305 _fn, lineno, col, message, msg_args = warning.getMessageData() |
306 _fn, lineno, col, message, msg_args = warning.getMessageData() |
306 lineFlags = extractLineFlags(lines[lineno - 1].strip()) |
307 lineFlags = extractLineFlags(lines[lineno - 1].strip()) |
307 try: |
308 with contextlib.suppress(IndexError): |
308 lineFlags += extractLineFlags(lines[lineno].strip(), |
309 lineFlags += extractLineFlags(lines[lineno].strip(), |
309 flagsLine=True) |
310 flagsLine=True) |
310 except IndexError: |
|
311 pass |
|
312 if ( |
311 if ( |
313 "__IGNORE_WARNING__" not in lineFlags and |
312 "__IGNORE_WARNING__" not in lineFlags and |
314 "noqa" not in lineFlags |
313 "noqa" not in lineFlags |
315 ): |
314 ): |
316 results.append((_fn, lineno, col, "", message, msg_args)) |
315 results.append((_fn, lineno, col, "", message, msg_args)) |
317 except SyntaxError as err: |
316 except SyntaxError as err: |
318 if err.text.strip(): |
317 msg = err.text.strip() if err.text.strip() else err.msg |
319 msg = err.text.strip() |
|
320 else: |
|
321 msg = err.msg |
|
322 results.append((filename, err.lineno, 0, "FLAKES_ERROR", msg, [])) |
318 results.append((filename, err.lineno, 0, "FLAKES_ERROR", msg, [])) |
323 |
319 |
324 return [{'warnings': results}] |
320 return [{'warnings': results}] |