67 @param code message code (string) |
69 @param code message code (string) |
68 @param check reference to the checker function (function) |
70 @param check reference to the checker function (function) |
69 @param args arguments for the message (list) |
71 @param args arguments for the message (list) |
70 @return error code (string) |
72 @return error code (string) |
71 """ |
73 """ |
72 code = super(CodeStyleCheckerReport, self).error_args( |
74 code = super().error_args( |
73 line_number, offset, code, check, *args) |
75 line_number, offset, code, check, *args) |
74 if code and (self.counters[code] == 1 or self.__repeat): |
76 if code and (self.counters[code] == 1 or self.__repeat): |
75 self.errors.append( |
77 self.errors.append( |
76 { |
78 { |
77 "file": self.filename, |
79 "file": self.filename, |
276 |
278 |
277 @param filename source filename |
279 @param filename source filename |
278 @type str |
280 @type str |
279 @param source string containing the code to check |
281 @param source string containing the code to check |
280 @type str |
282 @type str |
281 @return tuple containing the error dictionary with syntax error details |
283 @return tuple containing the error dictionary with syntax error details, |
282 and a statistics dictionary or a tuple containing two None |
284 a statistics dictionary and None or a tuple containing two None and |
283 @rtype tuple of (dict, dict) or tuple of (None, None) |
285 the generated AST tree |
|
286 @rtype tuple of (dict, dict, None) or tuple of (None, None, ast.Module) |
284 """ |
287 """ |
285 src = "".join(source) |
288 src = "".join(source) |
286 |
289 |
287 try: |
290 try: |
288 ast.parse(src, filename, 'exec') |
291 tree = ( |
289 return None, None |
292 ast.parse(src, filename, 'exec', type_comments=True) |
|
293 # need the 'type_comments' parameter to include type annotations |
|
294 if sys.version_info >= (3, 8) else |
|
295 ast.parse(src, filename, 'exec') |
|
296 ) |
|
297 return None, None, tree |
290 except (SyntaxError, TypeError): |
298 except (SyntaxError, TypeError): |
291 exc_type, exc = sys.exc_info()[:2] |
299 exc_type, exc = sys.exc_info()[:2] |
292 if len(exc.args) > 1: |
300 if len(exc.args) > 1: |
293 offset = exc.args[1] |
301 offset = exc.args[1] |
294 if len(offset) > 2: |
302 if len(offset) > 2: |
295 offset = offset[1:3] |
303 offset = offset[1:3] |
296 else: |
304 else: |
297 offset = (1, 0) |
305 offset = (1, 0) |
298 return ({ |
306 return ( |
299 "file": filename, |
307 { |
300 "line": offset[0], |
308 "file": filename, |
301 "offset": offset[1], |
309 "line": offset[0], |
302 "code": "E901", |
310 "offset": offset[1], |
303 "args": [exc_type.__name__, exc.args[0]], |
311 "code": "E901", |
304 }, { |
312 "args": [exc_type.__name__, exc.args[0]], |
305 "E901": 1, |
313 }, { |
306 }) |
314 "E901": 1, |
|
315 }, |
|
316 None |
|
317 ) |
307 |
318 |
308 |
319 |
309 def __checkCodeStyle(filename, source, args): |
320 def __checkCodeStyle(filename, source, args): |
310 """ |
321 """ |
311 Private module function to perform the code style check and/or fix |
322 Private module function to perform the code style check and/or fix |
364 ignore = [i.strip() for i in |
375 ignore = [i.strip() for i in |
365 excludeMessages.split(',') if i.strip()] |
376 excludeMessages.split(',') if i.strip()] |
366 else: |
377 else: |
367 ignore = [] |
378 ignore = [] |
368 |
379 |
369 syntaxError, syntaxStats = __checkSyntax(filename, source) |
380 syntaxError, syntaxStats, tree = __checkSyntax(filename, source) |
370 if syntaxError: |
381 |
371 errors = [syntaxError] |
382 # perform the checks only, if syntax is ok and AST tree was generated |
372 stats.update(syntaxStats) |
383 if tree: |
373 |
|
374 # perform the checks only, if syntax is ok |
|
375 else: |
|
376 # check coding style |
384 # check coding style |
377 pycodestyle.BLANK_LINES_CONFIG = { |
385 pycodestyle.BLANK_LINES_CONFIG = { |
378 # Top level class and function. |
386 # Top level class and function. |
379 'top_level': blankLines[0], |
387 'top_level': blankLines[0], |
380 # Methods and nested class and function. |
388 # Methods and nested class and function. |
401 stats.update(docStyleChecker.counters) |
409 stats.update(docStyleChecker.counters) |
402 errors += docStyleChecker.errors |
410 errors += docStyleChecker.errors |
403 |
411 |
404 # miscellaneous additional checks |
412 # miscellaneous additional checks |
405 miscellaneousChecker = MiscellaneousChecker( |
413 miscellaneousChecker = MiscellaneousChecker( |
406 source, filename, select, ignore, [], repeatMessages, |
414 source, filename, tree, select, ignore, [], repeatMessages, |
407 miscellaneousArgs) |
415 miscellaneousArgs) |
408 miscellaneousChecker.run() |
416 miscellaneousChecker.run() |
409 stats.update(miscellaneousChecker.counters) |
417 stats.update(miscellaneousChecker.counters) |
410 errors += miscellaneousChecker.errors |
418 errors += miscellaneousChecker.errors |
411 |
419 |
412 # check code complexity |
420 # check code complexity |
413 complexityChecker = ComplexityChecker( |
421 complexityChecker = ComplexityChecker( |
414 source, filename, select, ignore, codeComplexityArgs) |
422 source, filename, tree, select, ignore, codeComplexityArgs) |
415 complexityChecker.run() |
423 complexityChecker.run() |
416 stats.update(complexityChecker.counters) |
424 stats.update(complexityChecker.counters) |
417 errors += complexityChecker.errors |
425 errors += complexityChecker.errors |
418 |
426 |
419 # check function annotations |
427 # check function annotations |
420 if sys.version_info >= (3, 5, 0): |
428 if sys.version_info >= (3, 8, 0): |
421 # annotations are supported from Python 3.5 on |
429 # annotations with type comments are supported from |
|
430 # Python 3.8 on |
422 from Annotations.AnnotationsChecker import AnnotationsChecker |
431 from Annotations.AnnotationsChecker import AnnotationsChecker |
423 annotationsChecker = AnnotationsChecker( |
432 annotationsChecker = AnnotationsChecker( |
424 source, filename, select, ignore, [], repeatMessages, |
433 source, filename, tree, select, ignore, [], repeatMessages, |
425 annotationArgs) |
434 annotationArgs) |
426 annotationsChecker.run() |
435 annotationsChecker.run() |
427 stats.update(annotationsChecker.counters) |
436 stats.update(annotationsChecker.counters) |
428 errors += annotationsChecker.errors |
437 errors += annotationsChecker.errors |
429 |
438 |
|
439 # check for security issues |
430 securityChecker = SecurityChecker( |
440 securityChecker = SecurityChecker( |
431 source, filename, select, ignore, [], repeatMessages, |
441 source, filename, tree, select, ignore, [], repeatMessages, |
432 securityArgs) |
442 securityArgs) |
433 securityChecker.run() |
443 securityChecker.run() |
434 stats.update(securityChecker.counters) |
444 stats.update(securityChecker.counters) |
435 errors += securityChecker.errors |
445 errors += securityChecker.errors |
436 |
446 |
|
447 # check for pathlib usage |
437 pathlibChecker = PathlibChecker( |
448 pathlibChecker = PathlibChecker( |
438 source, filename, select, ignore, [], repeatMessages) |
449 source, filename, tree, select, ignore, [], repeatMessages) |
439 pathlibChecker.run() |
450 pathlibChecker.run() |
440 stats.update(pathlibChecker.counters) |
451 stats.update(pathlibChecker.counters) |
441 errors += pathlibChecker.errors |
452 errors += pathlibChecker.errors |
|
453 |
|
454 # check for code simplifications |
|
455 simplifyChecker = SimplifyChecker( |
|
456 source, filename, tree, select, ignore, [], repeatMessages) |
|
457 simplifyChecker.run() |
|
458 stats.update(simplifyChecker.counters) |
|
459 errors += simplifyChecker.errors |
|
460 |
|
461 elif syntaxError: |
|
462 errors = [syntaxError] |
|
463 stats.update(syntaxStats) |
442 |
464 |
443 errorsDict = {} |
465 errorsDict = {} |
444 for error in errors: |
466 for error in errors: |
445 if error["line"] > len(source): |
467 if error["line"] > len(source): |
446 error["line"] = len(source) |
468 error["line"] = len(source) |