src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9219
964a326c58d4
child 9233
d14a2ec006a7
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
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
192 195
193 196
194 def codeStyleBatchCheck(argumentsList, send, fx, cancelled, maxProcesses=0): 197 def codeStyleBatchCheck(argumentsList, send, fx, cancelled, maxProcesses=0):
195 """ 198 """
196 Module function to check code style for a batch of files. 199 Module function to check code style for a batch of files.
197 200
198 @param argumentsList list of arguments tuples as given for codeStyleCheck 201 @param argumentsList list of arguments tuples as given for codeStyleCheck
199 @type list 202 @type list
200 @param send reference to send function 203 @param send reference to send function
201 @type func 204 @type func
202 @param fx registered service name 205 @param fx registered service name
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:
317 "file": filename, 319 "file": filename,
318 "line": offset[0], 320 "line": offset[0],
319 "offset": offset[1], 321 "offset": offset[1],
320 "code": "E901", 322 "code": "E901",
321 "args": [exc_type.__name__, exc.args[0]], 323 "args": [exc_type.__name__, exc.args[0]],
322 }, { 324 },
325 {
323 "E901": 1, 326 "E901": 1,
324 }, 327 },
325 None 328 None,
326 ) 329 )
327 330
328 331
329 def __checkCodeStyle(filename, source, args): 332 def __checkCodeStyle(filename, source, args):
330 """ 333 """
331 Private module function to perform the code style check and/or fix 334 Private module function to perform the code style check and/or fix
332 found errors. 335 found errors.
333 336
334 @param filename source filename 337 @param filename source filename
335 @type str 338 @type str
336 @param source string containing the code to check 339 @param source string containing the code to check
337 @type str 340 @type str
338 @param args arguments used by the codeStyleCheck function (list of 341 @param args arguments used by the codeStyleCheck function (list of
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

eric ide

mercurial