--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsFunctionVisitor.py Wed Jul 27 18:02:43 2022 +0200 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsFunctionVisitor.py Thu Jul 28 14:19:57 2022 +0200 @@ -8,7 +8,7 @@ """ # -# The visitor and associated classes are adapted from flake8-annotations v2.7.0 +# The visitor and associated classes are adapted from flake8-annotations v2.9.0 # import ast @@ -35,6 +35,7 @@ hasTypeAnnotation=False, has3107Annotation=False, hasTypeComment=False, + isDynamicallyTyped=False, ): """ Constructor @@ -56,6 +57,8 @@ @param hasTypeComment flag indicating the presence of a type comment (defaults to False) @type bool (optional) + @param isDynamicallyTyped flag indicating dynamic typing (defaults to False) + @type bool (optional) """ self.argname = argname self.lineno = lineno @@ -64,6 +67,7 @@ self.hasTypeAnnotation = hasTypeAnnotation self.has3107Annotation = has3107Annotation self.hasTypeComment = hasTypeComment + self.isDynamicallyTyped = isDynamicallyTyped @classmethod def fromNode(cls, node, annotationTypeName): @@ -89,8 +93,43 @@ newArg.hasTypeAnnotation = True newArg.hasTypeComment = True + if cls._isAnnotatedAny(node.type_comment): + newArg.isDynamicallyTyped = True + return newArg + @staticmethod + def _isAnnotatedAny(argExpr): + """ + Static method to check if the provided expression node is annotated with + 'typing.Any'. + + Support is provided for the following patterns: + * 'from typing import Any; foo: Any' + * 'import typing; foo: typing.Any' + * 'import typing as <alias>; foo: <alias>.Any' + + Type comments are also supported. Inline type comments are assumed to be + passed here as 'str', and function-level type comments are assumed to be + passed as 'ast.expr'. + + @param argExpr DESCRIPTION + @type ast.expr or str + @return flag indicating an annotation with 'typing.Any' + @rtype bool + """ + if isinstance(argExpr, ast.Name): + if argExpr.id == "Any": + return True + elif isinstance(argExpr, ast.Attribute): + if argExpr.attr == "Any": + return True + elif isinstance(argExpr, str): # __IGNORE_WARNING_Y102__ + if argExpr.split(".", maxsplit=1)[-1] == "Any": + return True + + return False + class Function: """ @@ -319,6 +358,9 @@ returnArg.has3107Annotation = True newFunction.isReturnAnnotated = True + if Argument._isAnnotatedAny(node.returns): + returnArg.isDynamicallyTyped = True + newFunction.args.append(returnArg) # Type comments in-line with input arguments are handled by the @@ -426,10 +468,15 @@ arg.hasTypeAnnotation = True arg.hasTypeComment = True + if Argument._isAnnotatedAny(hintComment): + arg.isDynamicallyTyped = True + # Return arg is always last funcObj.args[-1].hasTypeAnnotation = True funcObj.args[-1].hasTypeComment = True funcObj.isReturnAnnotated = True + if Argument._isAnnotatedAny(hintTree.returns): + arg.isDynamicallyTyped = True return funcObj @@ -476,9 +523,9 @@ # Short circuit return hintTree - if funcObj.classDecoratorType != ClassDecoratorType.STATICMETHOD and len( - hintTree.argtypes - ) < (len(funcObj.args) - 1): + if funcObj.classDecoratorType != ClassDecoratorType.STATICMETHOD and ( + len(hintTree.argtypes) < (len(funcObj.args) - 1) + ): # Subtract 1 to skip return arg hintTree.argtypes = [ast.Ellipsis()] + hintTree.argtypes