29 |
29 |
30 |
30 |
31 def initService(): |
31 def initService(): |
32 """ |
32 """ |
33 Initialize the service and return the entry point. |
33 Initialize the service and return the entry point. |
34 |
34 |
35 @return the entry point for the background client (function) |
35 @return the entry point for the background client (function) |
36 """ |
36 """ |
37 return codeStyleCheck |
37 return codeStyleCheck |
38 |
38 |
39 |
39 |
40 def initBatchService(): |
40 def initBatchService(): |
41 """ |
41 """ |
42 Initialize the batch service and return the entry point. |
42 Initialize the batch service and return the entry point. |
43 |
43 |
44 @return the entry point for the background client (function) |
44 @return the entry point for the background client (function) |
45 """ |
45 """ |
46 return codeStyleBatchCheck |
46 return codeStyleBatchCheck |
47 |
47 |
48 |
48 |
49 class CodeStyleCheckerReport(pycodestyle.BaseReport): |
49 class CodeStyleCheckerReport(pycodestyle.BaseReport): |
50 """ |
50 """ |
51 Class implementing a special report to be used with our dialog. |
51 Class implementing a special report to be used with our dialog. |
52 """ |
52 """ |
|
53 |
53 def __init__(self, options): |
54 def __init__(self, options): |
54 """ |
55 """ |
55 Constructor |
56 Constructor |
56 |
57 |
57 @param options options for the report (optparse.Values) |
58 @param options options for the report (optparse.Values) |
58 """ |
59 """ |
59 super().__init__(options) |
60 super().__init__(options) |
60 |
61 |
61 self.__repeat = options.repeat |
62 self.__repeat = options.repeat |
62 self.errors = [] |
63 self.errors = [] |
63 |
64 |
64 def error_args(self, line_number, offset, code, check, *args): |
65 def error_args(self, line_number, offset, code, check, *args): |
65 """ |
66 """ |
66 Public method to collect the error messages. |
67 Public method to collect the error messages. |
67 |
68 |
68 @param line_number line number of the issue (integer) |
69 @param line_number line number of the issue (integer) |
69 @param offset position within line of the issue (integer) |
70 @param offset position within line of the issue (integer) |
70 @param code message code (string) |
71 @param code message code (string) |
71 @param check reference to the checker function (function) |
72 @param check reference to the checker function (function) |
72 @param args arguments for the message (list) |
73 @param args arguments for the message (list) |
73 @return error code (string) |
74 @return error code (string) |
74 """ |
75 """ |
75 code = super().error_args( |
76 code = super().error_args(line_number, offset, code, check, *args) |
76 line_number, offset, code, check, *args) |
|
77 if code and (self.counters[code] == 1 or self.__repeat): |
77 if code and (self.counters[code] == 1 or self.__repeat): |
78 self.errors.append( |
78 self.errors.append( |
79 { |
79 { |
80 "file": self.filename, |
80 "file": self.filename, |
81 "line": line_number, |
81 "line": line_number, |
89 |
89 |
90 def extractLineFlags(line, startComment="#", endComment="", flagsLine=False): |
90 def extractLineFlags(line, startComment="#", endComment="", flagsLine=False): |
91 """ |
91 """ |
92 Function to extract flags starting and ending with '__' from a line |
92 Function to extract flags starting and ending with '__' from a line |
93 comment. |
93 comment. |
94 |
94 |
95 @param line line to extract flags from (string) |
95 @param line line to extract flags from (string) |
96 @param startComment string identifying the start of the comment (string) |
96 @param startComment string identifying the start of the comment (string) |
97 @param endComment string identifying the end of a comment (string) |
97 @param endComment string identifying the end of a comment (string) |
98 @param flagsLine flag indicating to check for a flags only line (bool) |
98 @param flagsLine flag indicating to check for a flags only line (bool) |
99 @return list containing the extracted flags (list of strings) |
99 @return list containing the extracted flags (list of strings) |
100 """ |
100 """ |
101 flags = [] |
101 flags = [] |
102 |
102 |
103 if not flagsLine or ( |
103 if not flagsLine or (flagsLine and line.strip().startswith(startComment)): |
104 flagsLine and line.strip().startswith(startComment)): |
|
105 pos = line.rfind(startComment) |
104 pos = line.rfind(startComment) |
106 if pos >= 0: |
105 if pos >= 0: |
107 comment = line[pos + len(startComment):].strip() |
106 comment = line[pos + len(startComment) :].strip() |
108 if endComment: |
107 if endComment: |
109 endPos = line.rfind(endComment) |
108 endPos = line.rfind(endComment) |
110 if endPos >= 0: |
109 if endPos >= 0: |
111 comment = comment[:endPos] |
110 comment = comment[:endPos] |
112 flags = [f.strip() for f in comment.split() |
111 flags = [ |
113 if (f.startswith("__") and f.endswith("__"))] |
112 f.strip() |
114 flags += [f.strip().lower() for f in comment.split() |
113 for f in comment.split() |
115 if f in ("noqa", "NOQA", |
114 if (f.startswith("__") and f.endswith("__")) |
116 "nosec", "NOSEC", |
115 ] |
117 "secok", "SECOK")] |
116 flags += [ |
|
117 f.strip().lower() |
|
118 for f in comment.split() |
|
119 if f in ("noqa", "NOQA", "nosec", "NOSEC", "secok", "SECOK") |
|
120 ] |
118 return flags |
121 return flags |
119 |
122 |
120 |
123 |
121 def ignoreCode(code, lineFlags): |
124 def ignoreCode(code, lineFlags): |
122 """ |
125 """ |
123 Function to check, if the given code should be ignored as per line flags. |
126 Function to check, if the given code should be ignored as per line flags. |
124 |
127 |
125 @param code error code to be checked |
128 @param code error code to be checked |
126 @type str |
129 @type str |
127 @param lineFlags list of line flags to check against |
130 @param lineFlags list of line flags to check against |
128 @type list of str |
131 @type list of str |
129 @return flag indicating to ignore the code |
132 @return flag indicating to ignore the code |
130 @rtype bool |
133 @rtype bool |
131 """ |
134 """ |
132 if lineFlags: |
135 if lineFlags: |
133 |
136 |
134 if ( |
137 if ( |
135 "__IGNORE_WARNING__" in lineFlags or |
138 "__IGNORE_WARNING__" in lineFlags |
136 "noqa" in lineFlags or |
139 or "noqa" in lineFlags |
137 "nosec" in lineFlags |
140 or "nosec" in lineFlags |
138 ): |
141 ): |
139 # ignore all warning codes |
142 # ignore all warning codes |
140 return True |
143 return True |
141 |
144 |
142 for flag in lineFlags: |
145 for flag in lineFlags: |
143 # check individual warning code |
146 # check individual warning code |
144 if flag.startswith("__IGNORE_WARNING_"): |
147 if flag.startswith("__IGNORE_WARNING_"): |
145 ignoredCode = flag[2:-2].rsplit("_", 1)[-1] |
148 ignoredCode = flag[2:-2].rsplit("_", 1)[-1] |
146 if code.startswith(ignoredCode): |
149 if code.startswith(ignoredCode): |
147 return True |
150 return True |
148 |
151 |
149 return False |
152 return False |
150 |
153 |
151 |
154 |
152 def securityOk(code, lineFlags): |
155 def securityOk(code, lineFlags): |
153 """ |
156 """ |
154 Function to check, if the given code is an acknowledged security report. |
157 Function to check, if the given code is an acknowledged security report. |
155 |
158 |
156 @param code error code to be checked |
159 @param code error code to be checked |
157 @type str |
160 @type str |
158 @param lineFlags list of line flags to check against |
161 @param lineFlags list of line flags to check against |
159 @type list of str |
162 @type list of str |
160 @return flag indicating an acknowledged security report |
163 @return flag indicating an acknowledged security report |
161 @rtype bool |
164 @rtype bool |
162 """ |
165 """ |
163 if lineFlags: |
166 if lineFlags: |
164 return "secok" in lineFlags |
167 return "secok" in lineFlags |
165 |
168 |
166 return False |
169 return False |
167 |
170 |
168 |
171 |
169 def codeStyleCheck(filename, source, args): |
172 def codeStyleCheck(filename, source, args): |
170 """ |
173 """ |
171 Do the code style check and/or fix found errors. |
174 Do the code style check and/or fix found errors. |
172 |
175 |
173 @param filename source filename |
176 @param filename source filename |
174 @type str |
177 @type str |
175 @param source string containing the code to check |
178 @param source string containing the code to check |
176 @type str |
179 @type str |
177 @param args arguments used by the codeStyleCheck function (list of |
180 @param args arguments used by the codeStyleCheck function (list of |
226 for _ in range(initialTasks): |
229 for _ in range(initialTasks): |
227 taskQueue.put(argumentsList.pop(0)) |
230 taskQueue.put(argumentsList.pop(0)) |
228 |
231 |
229 # Start worker processes |
232 # Start worker processes |
230 workers = [ |
233 workers = [ |
231 multiprocessing.Process( |
234 multiprocessing.Process(target=workerTask, args=(taskQueue, doneQueue)) |
232 target=workerTask, args=(taskQueue, doneQueue) |
235 for _ in range(NumberOfProcesses) |
233 ) for _ in range(NumberOfProcesses) |
|
234 ] |
236 ] |
235 for worker in workers: |
237 for worker in workers: |
236 worker.start() |
238 worker.start() |
237 |
239 |
238 # Get and send results |
240 # Get and send results |
239 endIndex = len(argumentsList) - initialTasks |
241 endIndex = len(argumentsList) - initialTasks |
240 for i in range(len(argumentsList)): |
242 for i in range(len(argumentsList)): |
241 resultSent = False |
243 resultSent = False |
242 wasCancelled = False |
244 wasCancelled = False |
243 |
245 |
244 while not resultSent: |
246 while not resultSent: |
245 try: |
247 try: |
246 # get result (waiting max. 3 seconds and send it to frontend |
248 # get result (waiting max. 3 seconds and send it to frontend |
247 filename, result = doneQueue.get(timeout=3) |
249 filename, result = doneQueue.get(timeout=3) |
248 send(fx, filename, result) |
250 send(fx, filename, result) |
250 except queue.Empty: |
252 except queue.Empty: |
251 # ignore empty queue, just carry on |
253 # ignore empty queue, just carry on |
252 if cancelled(): |
254 if cancelled(): |
253 wasCancelled = True |
255 wasCancelled = True |
254 break |
256 break |
255 |
257 |
256 if wasCancelled or cancelled(): |
258 if wasCancelled or cancelled(): |
257 # just exit the loop ignoring the results of queued tasks |
259 # just exit the loop ignoring the results of queued tasks |
258 break |
260 break |
259 |
261 |
260 if i < endIndex: |
262 if i < endIndex: |
261 taskQueue.put(argumentsList.pop(0)) |
263 taskQueue.put(argumentsList.pop(0)) |
262 |
264 |
263 # Tell child processes to stop |
265 # Tell child processes to stop |
264 for _ in range(NumberOfProcesses): |
266 for _ in range(NumberOfProcesses): |
265 taskQueue.put('STOP') |
267 taskQueue.put("STOP") |
266 |
268 |
267 for worker in workers: |
269 for worker in workers: |
268 worker.join() |
270 worker.join() |
269 worker.close() |
271 worker.close() |
270 |
272 |
271 |
273 |
272 def workerTask(inputQueue, outputQueue): |
274 def workerTask(inputQueue, outputQueue): |
273 """ |
275 """ |
274 Module function acting as the parallel worker for the style check. |
276 Module function acting as the parallel worker for the style check. |
275 |
277 |
276 @param inputQueue input queue (multiprocessing.Queue) |
278 @param inputQueue input queue (multiprocessing.Queue) |
277 @param outputQueue output queue (multiprocessing.Queue) |
279 @param outputQueue output queue (multiprocessing.Queue) |
278 """ |
280 """ |
279 for filename, source, args in iter(inputQueue.get, 'STOP'): |
281 for filename, source, args in iter(inputQueue.get, "STOP"): |
280 result = __checkCodeStyle(filename, source, args) |
282 result = __checkCodeStyle(filename, source, args) |
281 outputQueue.put((filename, result)) |
283 outputQueue.put((filename, result)) |
282 |
284 |
283 |
285 |
284 def __checkSyntax(filename, source): |
286 def __checkSyntax(filename, source): |
285 """ |
287 """ |
286 Private module function to perform a syntax check. |
288 Private module function to perform a syntax check. |
287 |
289 |
288 @param filename source filename |
290 @param filename source filename |
289 @type str |
291 @type str |
290 @param source string containing the code to check |
292 @param source string containing the code to check |
291 @type str |
293 @type str |
292 @return tuple containing the error dictionary with syntax error details, |
294 @return tuple containing the error dictionary with syntax error details, |
293 a statistics dictionary and None or a tuple containing two None and |
295 a statistics dictionary and None or a tuple containing two None and |
294 the generated AST tree |
296 the generated AST tree |
295 @rtype tuple of (dict, dict, None) or tuple of (None, None, ast.Module) |
297 @rtype tuple of (dict, dict, None) or tuple of (None, None, ast.Module) |
296 """ |
298 """ |
297 src = "".join(source) |
299 src = "".join(source) |
298 |
300 |
299 try: |
301 try: |
300 tree = ( |
302 tree = ( |
301 ast.parse(src, filename, 'exec', type_comments=True) |
303 ast.parse(src, filename, "exec", type_comments=True) |
302 # need the 'type_comments' parameter to include type annotations |
304 # need the 'type_comments' parameter to include type annotations |
303 if sys.version_info >= (3, 8) else |
305 if sys.version_info >= (3, 8) |
304 ast.parse(src, filename, 'exec') |
306 else ast.parse(src, filename, "exec") |
305 ) |
307 ) |
306 return None, None, tree |
308 return None, None, tree |
307 except (SyntaxError, TypeError): |
309 except (SyntaxError, TypeError): |
308 exc_type, exc = sys.exc_info()[:2] |
310 exc_type, exc = sys.exc_info()[:2] |
309 if len(exc.args) > 1: |
311 if len(exc.args) > 1: |
357 <li>fixcode: message code for the fix</li> |
360 <li>fixcode: message code for the fix</li> |
358 <li>fixargs: list of arguments to format the fix message</li> |
361 <li>fixargs: list of arguments to format the fix message</li> |
359 </ul> |
362 </ul> |
360 @rtype tuple of (dict, list of dict) |
363 @rtype tuple of (dict, list of dict) |
361 """ |
364 """ |
362 (excludeMessages, includeMessages, repeatMessages, fixCodes, noFixCodes, |
365 ( |
363 fixIssues, maxLineLength, maxDocLineLength, blankLines, hangClosing, |
366 excludeMessages, |
364 docType, codeComplexityArgs, miscellaneousArgs, annotationArgs, |
367 includeMessages, |
365 securityArgs, importsArgs, errors, eol, encoding, backup) = args |
368 repeatMessages, |
366 |
369 fixCodes, |
|
370 noFixCodes, |
|
371 fixIssues, |
|
372 maxLineLength, |
|
373 maxDocLineLength, |
|
374 blankLines, |
|
375 hangClosing, |
|
376 docType, |
|
377 codeComplexityArgs, |
|
378 miscellaneousArgs, |
|
379 annotationArgs, |
|
380 securityArgs, |
|
381 importsArgs, |
|
382 errors, |
|
383 eol, |
|
384 encoding, |
|
385 backup, |
|
386 ) = args |
|
387 |
367 stats = {} |
388 stats = {} |
368 |
389 |
369 if fixIssues: |
390 if fixIssues: |
370 from CodeStyleFixer import CodeStyleFixer |
391 from CodeStyleFixer import CodeStyleFixer |
|
392 |
371 fixer = CodeStyleFixer( |
393 fixer = CodeStyleFixer( |
372 filename, source, fixCodes, noFixCodes, |
394 filename, |
373 maxLineLength, blankLines, True, eol, backup) |
395 source, |
|
396 fixCodes, |
|
397 noFixCodes, |
|
398 maxLineLength, |
|
399 blankLines, |
|
400 True, |
|
401 eol, |
|
402 backup, |
|
403 ) |
374 # always fix in place |
404 # always fix in place |
375 else: |
405 else: |
376 fixer = None |
406 fixer = None |
377 |
407 |
378 if not errors: |
408 if not errors: |
379 if includeMessages: |
409 if includeMessages: |
380 select = [s.strip() for s in |
410 select = [s.strip() for s in includeMessages.split(",") if s.strip()] |
381 includeMessages.split(',') if s.strip()] |
|
382 else: |
411 else: |
383 select = [] |
412 select = [] |
384 if excludeMessages: |
413 if excludeMessages: |
385 ignore = [i.strip() for i in |
414 ignore = [i.strip() for i in excludeMessages.split(",") if i.strip()] |
386 excludeMessages.split(',') if i.strip()] |
|
387 else: |
415 else: |
388 ignore = [] |
416 ignore = [] |
389 |
417 |
390 syntaxError, syntaxStats, tree = __checkSyntax(filename, source) |
418 syntaxError, syntaxStats, tree = __checkSyntax(filename, source) |
391 |
419 |
392 # perform the checks only, if syntax is ok and AST tree was generated |
420 # perform the checks only, if syntax is ok and AST tree was generated |
393 if tree: |
421 if tree: |
394 # check coding style |
422 # check coding style |
395 pycodestyle.BLANK_LINES_CONFIG = { |
423 pycodestyle.BLANK_LINES_CONFIG = { |
396 # Top level class and function. |
424 # Top level class and function. |
397 'top_level': blankLines[0], |
425 "top_level": blankLines[0], |
398 # Methods and nested class and function. |
426 # Methods and nested class and function. |
399 'method': blankLines[1], |
427 "method": blankLines[1], |
400 } |
428 } |
401 styleGuide = pycodestyle.StyleGuide( |
429 styleGuide = pycodestyle.StyleGuide( |
402 reporter=CodeStyleCheckerReport, |
430 reporter=CodeStyleCheckerReport, |
403 repeat=repeatMessages, |
431 repeat=repeatMessages, |
404 select=select, |
432 select=select, |
408 hang_closing=hangClosing, |
436 hang_closing=hangClosing, |
409 ) |
437 ) |
410 report = styleGuide.check_files([filename]) |
438 report = styleGuide.check_files([filename]) |
411 stats.update(report.counters) |
439 stats.update(report.counters) |
412 errors = report.errors |
440 errors = report.errors |
413 |
441 |
414 # check documentation style |
442 # check documentation style |
415 docStyleChecker = DocStyleChecker( |
443 docStyleChecker = DocStyleChecker( |
416 source, filename, select, ignore, [], repeatMessages, |
444 source, |
417 maxLineLength=maxDocLineLength, docType=docType) |
445 filename, |
|
446 select, |
|
447 ignore, |
|
448 [], |
|
449 repeatMessages, |
|
450 maxLineLength=maxDocLineLength, |
|
451 docType=docType, |
|
452 ) |
418 docStyleChecker.run() |
453 docStyleChecker.run() |
419 stats.update(docStyleChecker.counters) |
454 stats.update(docStyleChecker.counters) |
420 errors += docStyleChecker.errors |
455 errors += docStyleChecker.errors |
421 |
456 |
422 # miscellaneous additional checks |
457 # miscellaneous additional checks |
423 miscellaneousChecker = MiscellaneousChecker( |
458 miscellaneousChecker = MiscellaneousChecker( |
424 source, filename, tree, select, ignore, [], repeatMessages, |
459 source, |
425 miscellaneousArgs) |
460 filename, |
|
461 tree, |
|
462 select, |
|
463 ignore, |
|
464 [], |
|
465 repeatMessages, |
|
466 miscellaneousArgs, |
|
467 ) |
426 miscellaneousChecker.run() |
468 miscellaneousChecker.run() |
427 stats.update(miscellaneousChecker.counters) |
469 stats.update(miscellaneousChecker.counters) |
428 errors += miscellaneousChecker.errors |
470 errors += miscellaneousChecker.errors |
429 |
471 |
430 # check code complexity |
472 # check code complexity |
431 complexityChecker = ComplexityChecker( |
473 complexityChecker = ComplexityChecker( |
432 source, filename, tree, select, ignore, codeComplexityArgs) |
474 source, filename, tree, select, ignore, codeComplexityArgs |
|
475 ) |
433 complexityChecker.run() |
476 complexityChecker.run() |
434 stats.update(complexityChecker.counters) |
477 stats.update(complexityChecker.counters) |
435 errors += complexityChecker.errors |
478 errors += complexityChecker.errors |
436 |
479 |
437 # check function annotations |
480 # check function annotations |
438 if sys.version_info >= (3, 8, 0): |
481 if sys.version_info >= (3, 8, 0): |
439 # annotations with type comments are supported from |
482 # annotations with type comments are supported from |
440 # Python 3.8 on |
483 # Python 3.8 on |
441 from Annotations.AnnotationsChecker import AnnotationsChecker |
484 from Annotations.AnnotationsChecker import AnnotationsChecker |
|
485 |
442 annotationsChecker = AnnotationsChecker( |
486 annotationsChecker = AnnotationsChecker( |
443 source, filename, tree, select, ignore, [], repeatMessages, |
487 source, |
444 annotationArgs) |
488 filename, |
|
489 tree, |
|
490 select, |
|
491 ignore, |
|
492 [], |
|
493 repeatMessages, |
|
494 annotationArgs, |
|
495 ) |
445 annotationsChecker.run() |
496 annotationsChecker.run() |
446 stats.update(annotationsChecker.counters) |
497 stats.update(annotationsChecker.counters) |
447 errors += annotationsChecker.errors |
498 errors += annotationsChecker.errors |
448 |
499 |
449 # check for security issues |
500 # check for security issues |
450 securityChecker = SecurityChecker( |
501 securityChecker = SecurityChecker( |
451 source, filename, tree, select, ignore, [], repeatMessages, |
502 source, filename, tree, select, ignore, [], repeatMessages, securityArgs |
452 securityArgs) |
503 ) |
453 securityChecker.run() |
504 securityChecker.run() |
454 stats.update(securityChecker.counters) |
505 stats.update(securityChecker.counters) |
455 errors += securityChecker.errors |
506 errors += securityChecker.errors |
456 |
507 |
457 # check for pathlib usage |
508 # check for pathlib usage |
458 pathlibChecker = PathlibChecker( |
509 pathlibChecker = PathlibChecker( |
459 source, filename, tree, select, ignore, [], repeatMessages) |
510 source, filename, tree, select, ignore, [], repeatMessages |
|
511 ) |
460 pathlibChecker.run() |
512 pathlibChecker.run() |
461 stats.update(pathlibChecker.counters) |
513 stats.update(pathlibChecker.counters) |
462 errors += pathlibChecker.errors |
514 errors += pathlibChecker.errors |
463 |
515 |
464 # check for code simplifications |
516 # check for code simplifications |
465 simplifyChecker = SimplifyChecker( |
517 simplifyChecker = SimplifyChecker( |
466 source, filename, tree, select, ignore, [], repeatMessages) |
518 source, filename, tree, select, ignore, [], repeatMessages |
|
519 ) |
467 simplifyChecker.run() |
520 simplifyChecker.run() |
468 stats.update(simplifyChecker.counters) |
521 stats.update(simplifyChecker.counters) |
469 errors += simplifyChecker.errors |
522 errors += simplifyChecker.errors |
470 |
523 |
471 # check import statements |
524 # check import statements |
472 importsChecker = ImportsChecker( |
525 importsChecker = ImportsChecker( |
473 source, filename, tree, select, ignore, [], repeatMessages, |
526 source, filename, tree, select, ignore, [], repeatMessages, importsArgs |
474 importsArgs) |
527 ) |
475 importsChecker.run() |
528 importsChecker.run() |
476 stats.update(importsChecker.counters) |
529 stats.update(importsChecker.counters) |
477 errors += importsChecker.errors |
530 errors += importsChecker.errors |
478 |
531 |
479 elif syntaxError: |
532 elif syntaxError: |
480 errors = [syntaxError] |
533 errors = [syntaxError] |
481 stats.update(syntaxStats) |
534 stats.update(syntaxStats) |
482 |
535 |
483 errorsDict = {} |
536 errorsDict = {} |
484 for error in errors: |
537 for error in errors: |
485 if error["line"] > len(source): |
538 if error["line"] > len(source): |
486 error["line"] = len(source) |
539 error["line"] = len(source) |
487 # inverse processing of messages and fixes |
540 # inverse processing of messages and fixes |
490 deferredFixes = {} |
543 deferredFixes = {} |
491 results = [] |
544 results = [] |
492 for lineno, errorsList in errorsDict.items(): |
545 for lineno, errorsList in errorsDict.items(): |
493 errorsList.sort(key=lambda x: x[0], reverse=True) |
546 errorsList.sort(key=lambda x: x[0], reverse=True) |
494 for _, error in errorsList: |
547 for _, error in errorsList: |
495 error.update({ |
548 error.update( |
496 "ignored": False, |
549 { |
497 "fixed": False, |
550 "ignored": False, |
498 "autofixing": False, |
551 "fixed": False, |
499 "fixcode": "", |
552 "autofixing": False, |
500 "fixargs": [], |
553 "fixcode": "", |
501 "securityOk": False, |
554 "fixargs": [], |
502 }) |
555 "securityOk": False, |
503 |
556 } |
|
557 ) |
|
558 |
504 if source: |
559 if source: |
505 code = error["code"] |
560 code = error["code"] |
506 lineFlags = extractLineFlags(source[lineno - 1].strip()) |
561 lineFlags = extractLineFlags(source[lineno - 1].strip()) |
507 with contextlib.suppress(IndexError): |
562 with contextlib.suppress(IndexError): |
508 lineFlags += extractLineFlags(source[lineno].strip(), |
563 lineFlags += extractLineFlags( |
509 flagsLine=True) |
564 source[lineno].strip(), flagsLine=True |
510 |
565 ) |
|
566 |
511 if securityOk(code, lineFlags): |
567 if securityOk(code, lineFlags): |
512 error["securityOk"] = True |
568 error["securityOk"] = True |
513 |
569 |
514 if ignoreCode(code, lineFlags): |
570 if ignoreCode(code, lineFlags): |
515 error["ignored"] = True |
571 error["ignored"] = True |
516 else: |
572 else: |
517 if fixer: |
573 if fixer: |
518 res, fixcode, fixargs, id_ = fixer.fixIssue( |
574 res, fixcode, fixargs, id_ = fixer.fixIssue( |
519 lineno, error["offset"], code) |
575 lineno, error["offset"], code |
|
576 ) |
520 if res == -1: |
577 if res == -1: |
521 deferredFixes[id_] = error |
578 deferredFixes[id_] = error |
522 else: |
579 else: |
523 error.update({ |
580 error.update( |
524 "fixed": res == 1, |
581 { |
525 "autofixing": True, |
582 "fixed": res == 1, |
526 "fixcode": fixcode, |
583 "autofixing": True, |
527 "fixargs": fixargs, |
584 "fixcode": fixcode, |
528 }) |
585 "fixargs": fixargs, |
529 |
586 } |
|
587 ) |
|
588 |
530 results.append(error) |
589 results.append(error) |
531 |
590 |
532 if fixer: |
591 if fixer: |
533 deferredResults = fixer.finalize() |
592 deferredResults = fixer.finalize() |
534 for id_ in deferredResults: |
593 for id_ in deferredResults: |
535 fixed, fixcode, fixargs = deferredResults[id_] |
594 fixed, fixcode, fixargs = deferredResults[id_] |
536 error = deferredFixes[id_] |
595 error = deferredFixes[id_] |
537 error.update({ |
596 error.update( |
538 "ignored": False, |
597 { |
539 "fixed": fixed == 1, |
598 "ignored": False, |
540 "autofixing": True, |
599 "fixed": fixed == 1, |
541 "fixcode": fixcode, |
600 "autofixing": True, |
542 "fixargs": fixargs, |
601 "fixcode": fixcode, |
543 }) |
602 "fixargs": fixargs, |
|
603 } |
|
604 ) |
544 |
605 |
545 saveError = fixer.saveFile(encoding) |
606 saveError = fixer.saveFile(encoding) |
546 if saveError: |
607 if saveError: |
547 for error in results: |
608 for error in results: |
548 error.update({ |
609 error.update( |
549 "fixcode": saveError[0], |
610 { |
550 "fixargs": saveError[1], |
611 "fixcode": saveError[0], |
551 }) |
612 "fixargs": saveError[1], |
|
613 } |
|
614 ) |
552 |
615 |
553 return stats, results |
616 return stats, results |