99 "A203", |
103 "A203", |
100 "A204", |
104 "A204", |
101 "A205", |
105 "A205", |
102 "A206", |
106 "A206", |
103 "A301", |
107 "A301", |
|
108 "A401", |
104 ), |
109 ), |
105 ), |
110 ), |
106 (self.__checkAnnotationsFuture, ("A871",)), |
111 (self.__checkAnnotationsFuture, ("A871", "A872")), |
107 (self.__checkAnnotationsCoverage, ("A881",)), |
112 (self.__checkAnnotationsCoverage, ("A881",)), |
108 (self.__checkAnnotationComplexity, ("A891", "A892")), |
113 (self.__checkAnnotationComplexity, ("A891", "A892")), |
109 ] |
114 ] |
110 |
115 |
111 self.__checkers = [] |
116 self.__checkers = [] |
200 allowUntypedNested = self.__args.get( |
204 allowUntypedNested = self.__args.get( |
201 "AllowUntypedNested", AnnotationsCheckerDefaultArgs["AllowUntypedNested"] |
205 "AllowUntypedNested", AnnotationsCheckerDefaultArgs["AllowUntypedNested"] |
202 ) |
206 ) |
203 mypyInitReturn = self.__args.get( |
207 mypyInitReturn = self.__args.get( |
204 "MypyInitReturn", AnnotationsCheckerDefaultArgs["MypyInitReturn"] |
208 "MypyInitReturn", AnnotationsCheckerDefaultArgs["MypyInitReturn"] |
|
209 ) |
|
210 allowStarArgAny = self.__args.get( |
|
211 "AllowStarArgAny", AnnotationsCheckerDefaultArgs["AllowStarArgAny"] |
205 ) |
212 ) |
206 |
213 |
207 # Store decorator lists as sets for easier lookup |
214 # Store decorator lists as sets for easier lookup |
208 dispatchDecorators = set( |
215 dispatchDecorators = set( |
209 self.__args.get( |
216 self.__args.get( |
248 hasTypeComment = function.hasTypeComment |
255 hasTypeComment = function.hasTypeComment |
249 |
256 |
250 has3107Annotation = False |
257 has3107Annotation = False |
251 # PEP 3107 annotations are captured by the return arg |
258 # PEP 3107 annotations are captured by the return arg |
252 |
259 |
|
260 annotatedArgs = function.getAnnotatedArguments() |
|
261 |
253 # Iterate over annotated args to detect mixing of type annotations |
262 # Iterate over annotated args to detect mixing of type annotations |
254 # and type comments. Emit this only once per function definition |
263 # and type comments. Emit this only once per function definition |
255 for arg in function.getAnnotatedArguments(): |
264 for arg in annotatedArgs: |
256 if arg.hasTypeComment: |
265 if arg.hasTypeComment: |
257 hasTypeComment = True |
266 hasTypeComment = True |
258 |
267 |
259 if arg.has3107Annotation: |
268 if arg.has3107Annotation: |
260 has3107Annotation = True |
269 has3107Annotation = True |
262 if hasTypeComment and has3107Annotation: |
271 if hasTypeComment and has3107Annotation: |
263 # Short-circuit check for mixing of type comments & |
272 # Short-circuit check for mixing of type comments & |
264 # 3107-style annotations |
273 # 3107-style annotations |
265 self.__error(function.lineno - 1, function.col_offset, "A301") |
274 self.__error(function.lineno - 1, function.col_offset, "A301") |
266 break |
275 break |
|
276 |
|
277 # Iterate over the annotated args to look for 'typing.Any' annotations |
|
278 # We could combine this with the above loop but I'd rather not add even |
|
279 # more sentinels unless we'd notice a significant enough performance impact |
|
280 for arg in annotatedArgs: |
|
281 if arg.isDynamicallyTyped: |
|
282 if allowStarArgAny and arg.annotationType in { |
|
283 AnnotationType.VARARG, |
|
284 AnnotationType.KWARG, |
|
285 }: |
|
286 continue |
|
287 |
|
288 self.__error(function.lineno - 1, function.col_offset, "A401") |
267 |
289 |
268 # Before we iterate over the function's missing annotations, check |
290 # Before we iterate over the function's missing annotations, check |
269 # to see if it's the closing function def in a series of |
291 # to see if it's the closing function def in a series of |
270 # `typing.overload` decorated functions. |
292 # `typing.overload` decorated functions. |
271 if lastOverloadDecoratedFunctionName == function.name: |
293 if lastOverloadDecoratedFunctionName == function.name: |
417 return "A001" |
439 return "A001" |
418 |
440 |
419 ####################################################################### |
441 ####################################################################### |
420 ## Annotations Coverage |
442 ## Annotations Coverage |
421 ## |
443 ## |
422 ## adapted from: flake8-annotations-coverage v0.0.5 |
444 ## adapted from: flake8-annotations-coverage v0.0.6 |
423 ####################################################################### |
445 ####################################################################### |
424 # TODO: update to v0.0.6 |
|
425 |
446 |
426 def __checkAnnotationsCoverage(self): |
447 def __checkAnnotationsCoverage(self): |
427 """ |
448 """ |
428 Private method to check for function annotation coverage. |
449 Private method to check for function annotation coverage. |
429 """ |
450 """ |
486 ) |
510 ) |
487 |
511 |
488 ####################################################################### |
512 ####################################################################### |
489 ## Annotations Complexity |
513 ## Annotations Complexity |
490 ## |
514 ## |
491 ## adapted from: flake8-annotations-complexity v0.0.6 |
515 ## adapted from: flake8-annotations-complexity v0.0.7 |
492 ####################################################################### |
516 ####################################################################### |
493 # TODO: update to v0.0.7 |
|
494 |
517 |
495 def __checkAnnotationComplexity(self): |
518 def __checkAnnotationComplexity(self): |
496 """ |
519 """ |
497 Private method to check the type annotation complexity. |
520 Private method to check the type annotation complexity. |
498 """ |
521 """ |
509 for f in ast.walk(self.__tree) |
532 for f in ast.walk(self.__tree) |
510 if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef)) |
533 if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef)) |
511 ] |
534 ] |
512 for functionDef in functionDefs: |
535 for functionDef in functionDefs: |
513 typeAnnotations += list( |
536 typeAnnotations += list( |
514 filter(None, [a.annotation for a in functionDef.args.args]) |
537 filter(None, (a.annotation for a in functionDef.args.args)) |
515 ) |
538 ) |
516 if functionDef.returns: |
539 if functionDef.returns: |
517 typeAnnotations.append(functionDef.returns) |
540 typeAnnotations.append(functionDef.returns) |
518 typeAnnotations += [ |
541 typeAnnotations += [ |
519 a.annotation |
542 a.annotation |
556 if AstUtilities.isString(annotationNode): |
579 if AstUtilities.isString(annotationNode): |
557 try: |
580 try: |
558 annotationNode = ast.parse(annotationNode.s).body[0].value |
581 annotationNode = ast.parse(annotationNode.s).body[0].value |
559 except (SyntaxError, IndexError): |
582 except (SyntaxError, IndexError): |
560 return defaultComplexity |
583 return defaultComplexity |
|
584 |
|
585 complexity = defaultComplexity |
561 if isinstance(annotationNode, ast.Subscript): |
586 if isinstance(annotationNode, ast.Subscript): |
562 if sys.version_info >= (3, 9): |
587 if sys.version_info >= (3, 9): |
563 return defaultComplexity + self.__getAnnotationComplexity( |
588 complexity = defaultComplexity + self.__getAnnotationComplexity( |
564 annotationNode.slice |
589 annotationNode.slice |
565 ) |
590 ) |
566 else: |
591 else: |
567 return defaultComplexity + self.__getAnnotationComplexity( |
592 complexity = defaultComplexity + self.__getAnnotationComplexity( |
568 annotationNode.slice.value |
593 annotationNode.slice.value |
569 ) |
594 ) |
|
595 |
570 if isinstance(annotationNode, ast.Tuple): |
596 if isinstance(annotationNode, ast.Tuple): |
571 return max( |
597 complexity = max( |
572 (self.__getAnnotationComplexity(n) for n in annotationNode.elts), |
598 (self.__getAnnotationComplexity(n) for n in annotationNode.elts), |
573 default=defaultComplexity, |
599 default=defaultComplexity, |
574 ) |
600 ) |
575 return defaultComplexity |
601 |
|
602 return complexity |
576 |
603 |
577 def __getAnnotationLength(self, annotationNode): |
604 def __getAnnotationLength(self, annotationNode): |
578 """ |
605 """ |
579 Private method to determine the annotation length. |
606 Private method to determine the annotation length. |
580 |
607 |
582 length for |
609 length for |
583 @type ast.AST |
610 @type ast.AST |
584 @return annotation length |
611 @return annotation length |
585 @rtype = int |
612 @rtype = int |
586 """ |
613 """ |
|
614 annotationLength = 0 |
587 if AstUtilities.isString(annotationNode): |
615 if AstUtilities.isString(annotationNode): |
588 try: |
616 try: |
589 annotationNode = ast.parse(annotationNode.s).body[0].value |
617 annotationNode = ast.parse(annotationNode.s).body[0].value |
590 except (SyntaxError, IndexError): |
618 except (SyntaxError, IndexError): |
591 return 0 |
619 return annotationLength |
|
620 |
592 if isinstance(annotationNode, ast.Subscript): |
621 if isinstance(annotationNode, ast.Subscript): |
593 try: |
622 with contextlib.suppress(AttributeError): |
594 if sys.version_info >= (3, 9): |
623 annotationLength = ( |
595 return len(annotationNode.slice.elts) |
624 len(annotationNode.slice.elts) |
596 else: |
625 if sys.version_info >= (3, 9) |
597 return len(annotationNode.slice.value.elts) |
626 else len(annotationNode.slice.value.elts) |
598 except AttributeError: |
627 ) |
599 return 0 |
628 |
600 return 0 |
629 return annotationLength |
601 |
630 |
602 ####################################################################### |
631 ####################################################################### |
603 ## 'from __future__ import annotations' checck |
632 ## 'from __future__ import annotations' checck |
604 ## |
633 ## |
605 ## adapted from: flake8-future-annotations v0.0.4 |
634 ## adapted from: flake8-future-annotations v0.0.5 |
606 ####################################################################### |
635 ####################################################################### |
607 # TODO: update to v0.0.5 |
|
608 |
636 |
609 def __checkAnnotationsFuture(self): |
637 def __checkAnnotationsFuture(self): |
610 """ |
638 """ |
611 Private method to check the use of __future__ and typing imports. |
639 Private method to check the use of __future__ and typing imports. |
612 """ |
640 """ |
613 from .AnnotationsFutureVisitor import AnnotationsFutureVisitor |
641 from .AnnotationsFutureVisitor import AnnotationsFutureVisitor |
|
642 |
|
643 forceFutureAnnotations = self.__args.get( |
|
644 "ForceFutureAnnotations", |
|
645 AnnotationsCheckerDefaultArgs["ForceFutureAnnotations"], |
|
646 ) |
614 |
647 |
615 visitor = AnnotationsFutureVisitor() |
648 visitor = AnnotationsFutureVisitor() |
616 visitor.visit(self.__tree) |
649 visitor.visit(self.__tree) |
617 |
650 |
618 if visitor.importsFutureAnnotations() or not visitor.hasTypingImports(): |
651 if visitor.importsFutureAnnotations(): |
619 return |
652 return |
620 |
653 |
621 imports = ", ".join(visitor.getTypingImports()) |
654 if visitor.hasTypingImports(): |
622 self.__error(0, 0, "A871", imports) |
655 imports = ", ".join(visitor.getTypingImports()) |
|
656 self.__error(0, 0, "A871", imports) |
|
657 elif forceFutureAnnotations: |
|
658 self.__error(0, 0, "A872") |