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

branch
eric7
changeset 11147
dee6e106b4d3
parent 11145
d328a7b74fd8
child 11148
15e30f0c76a8
equal deleted inserted replaced
11146:59e04f7003e9 11147:dee6e106b4d3
25 Class implementing a checker for function type annotations. 25 Class implementing a checker for function type annotations.
26 """ 26 """
27 27
28 Codes = [ 28 Codes = [
29 ## Function Annotations 29 ## Function Annotations
30 "A001", 30 "A-001",
31 "A002", 31 "A-002",
32 "A003", 32 "A-003",
33 ## Method Annotations 33 ## Method Annotations
34 "A101", 34 "A-101",
35 "A102", 35 "A-102",
36 ## Return Annotations 36 ## Return Annotations
37 "A201", 37 "A-201",
38 "A202", 38 "A-202",
39 "A203", 39 "A-203",
40 "A204", 40 "A-204",
41 "A205", 41 "A-205",
42 "A206", 42 "A-206",
43 ## Dynamically typed annotations 43 ## Dynamically typed annotations
44 "A401", 44 "A-401",
45 ## Type comments 45 ## Type comments
46 "A402", 46 "A-402",
47 ## Annotations Future 47 ## Annotations Future
48 "A871", 48 "A-871",
49 "A872", 49 "A-872",
50 "A873", 50 "A-873",
51 ## Annotation Coverage 51 ## Annotation Coverage
52 "A881", 52 "A-881",
53 ## Annotation Complexity 53 ## Annotation Complexity
54 "A891", 54 "A-891",
55 "A892", 55 "A-892",
56 ## use of typing.Union (PEP 604) 56 ## use of typing.Union (PEP 604)
57 "A901", 57 "A-901",
58 ## deprecated 'typing' symbols (PEP 585) 58 ## deprecated 'typing' symbols (PEP 585)
59 "A911", 59 "A-911",
60 ] 60 ]
61 61
62 def __init__(self, source, filename, tree, select, ignore, expected, repeat, args): 62 def __init__(self, source, filename, tree, select, ignore, expected, repeat, args):
63 """ 63 """
64 Constructor 64 Constructor
97 97
98 checkersWithCodes = [ 98 checkersWithCodes = [
99 ( 99 (
100 self.__checkFunctionAnnotations, 100 self.__checkFunctionAnnotations,
101 ( 101 (
102 "A001", 102 "A-001",
103 "A002", 103 "A-002",
104 "A003", 104 "A-003",
105 "A101", 105 "A-101",
106 "A102", 106 "A-102",
107 "A201", 107 "A-201",
108 "A202", 108 "A-202",
109 "A203", 109 "A-203",
110 "A204", 110 "A-204",
111 "A205", 111 "A-205",
112 "A206", 112 "A-206",
113 "A401", 113 "A-401",
114 "A402", 114 "A-402",
115 ), 115 ),
116 ), 116 ),
117 (self.__checkAnnotationsFuture, ("A871", "A872", "A873")), 117 (self.__checkAnnotationsFuture, ("A-871", "A-872", "A-873")),
118 (self.__checkAnnotationsCoverage, ("A881",)), 118 (self.__checkAnnotationsCoverage, ("A-881",)),
119 (self.__checkAnnotationComplexity, ("A891", "A892")), 119 (self.__checkAnnotationComplexity, ("A-891", "A-892")),
120 (self.__checkAnnotationPep604, ("A901",)), 120 (self.__checkAnnotationPep604, ("A-901",)),
121 (self.__checkDeprecatedTypingSymbols, ("A911",)), 121 (self.__checkDeprecatedTypingSymbols, ("A-911",)),
122 ] 122 ]
123 123
124 self.__checkers = [] 124 self.__checkers = []
125 for checker, codes in checkersWithCodes: 125 for checker, codes in checkersWithCodes:
126 if any(not (code and self.__ignoreCode(code)) for code in codes): 126 if any(not (code and self.__ignoreCode(code)) for code in codes):
257 lastOverloadDecoratedFunctionName = None 257 lastOverloadDecoratedFunctionName = None
258 258
259 # Iterate over the arguments with missing type hints, by function. 259 # Iterate over the arguments with missing type hints, by function.
260 for function in visitor.functionDefinitions: 260 for function in visitor.functionDefinitions:
261 if function.hasTypeComment: 261 if function.hasTypeComment:
262 self.__error(function.lineno - 1, function.col_offset, "A402") 262 self.__error(function.lineno - 1, function.col_offset, "A-402")
263 263
264 if function.isDynamicallyTyped() and ( 264 if function.isDynamicallyTyped() and (
265 allowUntypedDefs or (function.isNested and allowUntypedNested) 265 allowUntypedDefs or (function.isNested and allowUntypedNested)
266 ): 266 ):
267 # Skip recording errors from dynamically typed functions 267 # Skip recording errors from dynamically typed functions
282 AnnotationType.VARARG, 282 AnnotationType.VARARG,
283 AnnotationType.KWARG, 283 AnnotationType.KWARG,
284 }: 284 }:
285 continue 285 continue
286 286
287 self.__error(function.lineno - 1, function.col_offset, "A401") 287 self.__error(function.lineno - 1, function.col_offset, "A-401")
288 288
289 # Before we iterate over the function's missing annotations, check 289 # Before we iterate over the function's missing annotations, check
290 # to see if it's the closing function def in a series of 290 # to see if it's the closing function def in a series of
291 # `typing.overload` decorated functions. 291 # `typing.overload` decorated functions.
292 if lastOverloadDecoratedFunctionName == function.name: 292 if lastOverloadDecoratedFunctionName == function.name:
315 # Record explicit errors for arguments that are missing annotations 315 # Record explicit errors for arguments that are missing annotations
316 for arg in function.getMissedAnnotations(): 316 for arg in function.getMissedAnnotations():
317 # Check for type comments here since we're not considering them as 317 # Check for type comments here since we're not considering them as
318 # typed args 318 # typed args
319 if arg.hasTypeComment: 319 if arg.hasTypeComment:
320 self.__error(arg.lineno - 1, arg.col_offset, "A402") 320 self.__error(arg.lineno - 1, arg.col_offset, "A-402")
321 321
322 if arg.argname == "return": 322 if arg.argname == "return":
323 # return annotations have multiple possible short-circuit 323 # return annotations have multiple possible short-circuit
324 # paths 324 # paths
325 if ( 325 if (
381 isFirstArg, 381 isFirstArg,
382 function.classDecoratorType, 382 function.classDecoratorType,
383 arg.annotationType, 383 arg.annotationType,
384 ) 384 )
385 385
386 if errorCode in ("A001", "A002", "A003"): 386 if errorCode in ("A-001", "A-002", "A-003"):
387 self.__error(arg.lineno - 1, arg.col_offset, errorCode, arg.argname) 387 self.__error(arg.lineno - 1, arg.col_offset, errorCode, arg.argname)
388 else: 388 else:
389 self.__error(arg.lineno - 1, arg.col_offset, errorCode) 389 self.__error(arg.lineno - 1, arg.col_offset, errorCode)
390 390
391 @lru_cache() # __IGNORE_WARNING_M519__ 391 @lru_cache() # __IGNORE_WARNING_M519__
404 """ 404 """
405 # Decorated class methods (@classmethod, @staticmethod) have a higher 405 # Decorated class methods (@classmethod, @staticmethod) have a higher
406 # priority than the rest 406 # priority than the rest
407 if isClassMethod: 407 if isClassMethod:
408 if classDecoratorType == ClassDecoratorType.CLASSMETHOD: 408 if classDecoratorType == ClassDecoratorType.CLASSMETHOD:
409 return "A206" 409 return "A-206"
410 elif classDecoratorType == ClassDecoratorType.STATICMETHOD: 410 elif classDecoratorType == ClassDecoratorType.STATICMETHOD:
411 return "A205" 411 return "A-205"
412 412
413 if functionType == FunctionType.SPECIAL: 413 if functionType == FunctionType.SPECIAL:
414 return "A204" 414 return "A-204"
415 elif functionType == FunctionType.PRIVATE: 415 elif functionType == FunctionType.PRIVATE:
416 return "A203" 416 return "A-203"
417 elif functionType == FunctionType.PROTECTED: 417 elif functionType == FunctionType.PROTECTED:
418 return "A202" 418 return "A-202"
419 else: 419 else:
420 return "A201" 420 return "A-201"
421 421
422 @lru_cache() # __IGNORE_WARNING_M519__ 422 @lru_cache() # __IGNORE_WARNING_M519__
423 def __argumentErrorClassifier( 423 def __argumentErrorClassifier(
424 self, isClassMethod, isFirstArg, classDecoratorType, annotationType 424 self, isClassMethod, isFirstArg, classDecoratorType, annotationType
425 ): 425 ):
441 # deferred to final check 441 # deferred to final check
442 if isClassMethod and isFirstArg: 442 if isClassMethod and isFirstArg:
443 # The first function argument here would be an instance of self or 443 # The first function argument here would be an instance of self or
444 # class 444 # class
445 if classDecoratorType == ClassDecoratorType.CLASSMETHOD: 445 if classDecoratorType == ClassDecoratorType.CLASSMETHOD:
446 return "A102" 446 return "A-102"
447 elif classDecoratorType != ClassDecoratorType.STATICMETHOD: 447 elif classDecoratorType != ClassDecoratorType.STATICMETHOD:
448 # Regular class method 448 # Regular class method
449 return "A101" 449 return "A-101"
450 450
451 # Check for remaining codes 451 # Check for remaining codes
452 if annotationType == AnnotationType.KWARG: 452 if annotationType == AnnotationType.KWARG:
453 return "A003" 453 return "A-003"
454 elif annotationType == AnnotationType.VARARG: 454 elif annotationType == AnnotationType.VARARG:
455 return "A002" 455 return "A-002"
456 else: 456 else:
457 # Combine PosOnlyArgs, Args, and KwOnlyArgs 457 # Combine PosOnlyArgs, Args, and KwOnlyArgs
458 return "A001" 458 return "A-001"
459 459
460 ####################################################################### 460 #######################################################################
461 ## Annotations Coverage 461 ## Annotations Coverage
462 ## 462 ##
463 ## adapted from: flake8-annotations-coverage v0.0.6 463 ## adapted from: flake8-annotations-coverage v0.0.6
493 len(list(filter(None, functionDefAnnotationsInfo))) 493 len(list(filter(None, functionDefAnnotationsInfo)))
494 / len(functionDefAnnotationsInfo) 494 / len(functionDefAnnotationsInfo)
495 * 100 495 * 100
496 ) 496 )
497 if annotationsCoverage < minAnnotationsCoverage: 497 if annotationsCoverage < minAnnotationsCoverage:
498 self.__error(0, 0, "A881", annotationsCoverage) 498 self.__error(0, 0, "A-881", annotationsCoverage)
499 499
500 def __hasTypeAnnotations(self, funcNode): 500 def __hasTypeAnnotations(self, funcNode):
501 """ 501 """
502 Private method to check for type annotations. 502 Private method to check for type annotations.
503 503
566 complexity = self.__getAnnotationComplexity(annotation) 566 complexity = self.__getAnnotationComplexity(annotation)
567 if complexity > maxAnnotationComplexity: 567 if complexity > maxAnnotationComplexity:
568 self.__error( 568 self.__error(
569 annotation.lineno - 1, 569 annotation.lineno - 1,
570 annotation.col_offset, 570 annotation.col_offset,
571 "A891", 571 "A-891",
572 complexity, 572 complexity,
573 maxAnnotationComplexity, 573 maxAnnotationComplexity,
574 ) 574 )
575 575
576 annotationLength = self.__getAnnotationLength(annotation) 576 annotationLength = self.__getAnnotationLength(annotation)
577 if annotationLength > maxAnnotationLength: 577 if annotationLength > maxAnnotationLength:
578 self.__error( 578 self.__error(
579 annotation.lineno - 1, 579 annotation.lineno - 1,
580 annotation.col_offset, 580 annotation.col_offset,
581 "A892", 581 "A-892",
582 annotationLength, 582 annotationLength,
583 maxAnnotationLength, 583 maxAnnotationLength,
584 ) 584 )
585 585
586 def __getAnnotationComplexity(self, annotationNode, defaultComplexity=1): 586 def __getAnnotationComplexity(self, annotationNode, defaultComplexity=1):
678 if visitor.importsFutureAnnotations(): 678 if visitor.importsFutureAnnotations():
679 return 679 return
680 680
681 if visitor.hasTypingImports(): 681 if visitor.hasTypingImports():
682 imports = ", ".join(visitor.getTypingImports()) 682 imports = ", ".join(visitor.getTypingImports())
683 self.__error(0, 0, "A871", imports) 683 self.__error(0, 0, "A-871", imports)
684 elif forceFutureAnnotations: 684 elif forceFutureAnnotations:
685 self.__error(0, 0, "A872") 685 self.__error(0, 0, "A-872")
686 686
687 if checkFutureAnnotations and visitor.hasSimplifiedTypes(): 687 if checkFutureAnnotations and visitor.hasSimplifiedTypes():
688 simplifiedTypes = ", ".join(sorted(visitor.getSimplifiedTypes())) 688 simplifiedTypes = ", ".join(sorted(visitor.getSimplifiedTypes()))
689 self.__error(0, 0, "A873", simplifiedTypes) 689 self.__error(0, 0, "A-873", simplifiedTypes)
690 690
691 ####################################################################### 691 #######################################################################
692 ## check use of 'typing.Union' (see PEP 604) 692 ## check use of 'typing.Union' (see PEP 604)
693 ## 693 ##
694 ## adapted from: flake8-pep604 v1.1.0 694 ## adapted from: flake8-pep604 v1.1.0
706 706
707 visitor = AnnotationsUnionVisitor() 707 visitor = AnnotationsUnionVisitor()
708 visitor.visit(self.__tree) 708 visitor.visit(self.__tree)
709 709
710 for node in visitor.getIssues(): 710 for node in visitor.getIssues():
711 self.__error(node.lineno - 1, node.col_offset, "A901") 711 self.__error(node.lineno - 1, node.col_offset, "A-901")
712 712
713 ####################################################################### 713 #######################################################################
714 ## check use of 'typing.Union' (see PEP 604) 714 ## check use of 'typing.Union' (see PEP 604)
715 ## 715 ##
716 ## adapted from: flake8-pep585 v0.1.7 716 ## adapted from: flake8-pep585 v0.1.7
739 ) 739 )
740 ) 740 )
741 visitor.visit(self.__tree) 741 visitor.visit(self.__tree)
742 742
743 for node, (name, replacement) in visitor.getIssues(): 743 for node, (name, replacement) in visitor.getIssues():
744 self.__error(node.lineno - 1, node.col_offset, "A911", name, replacement) 744 self.__error(node.lineno - 1, node.col_offset, "A-911", name, replacement)

eric ide

mercurial