--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsFunctionVisitor.py Tue May 23 14:39:14 2023 +0200 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsFunctionVisitor.py Tue May 23 15:58:50 2023 +0200 @@ -7,18 +7,19 @@ Module implementing a node visitor for function type annotations. """ -# -# The visitor and associated classes are adapted from flake8-annotations v2.9.0 -# +##################################################################################### +## The visitor and associated classes are adapted from flake8-annotations v3.0.1 +##################################################################################### import ast -import itertools import sys from .AnnotationsEnums import AnnotationType, ClassDecoratorType, FunctionType # The order of AST_ARG_TYPES must match Python's grammar -AST_ARG_TYPES = ("posonlyargs", "args", "vararg", "kwonlyargs", "kwarg") +AST_ARG_TYPES = ("args", "vararg", "kwonlyargs", "kwarg") +if sys.version_info >= (3, 8, 0): + AST_ARG_TYPES = ("posonlyargs",) + AST_ARG_TYPES class Argument: @@ -33,7 +34,6 @@ col_offset, annotationType, hasTypeAnnotation=False, - has3107Annotation=False, hasTypeComment=False, isDynamicallyTyped=False, ): @@ -51,9 +51,6 @@ @param hasTypeAnnotation flag indicating the presence of a type annotation (defaults to False) @type bool (optional) - @param has3107Annotation flag indicating the presence of a PEP 3107 - annotation (defaults to False) - @type bool (optional) @param hasTypeComment flag indicating the presence of a type comment (defaults to False) @type bool (optional) @@ -65,7 +62,6 @@ self.col_offset = col_offset self.annotationType = annotationType self.hasTypeAnnotation = hasTypeAnnotation - self.has3107Annotation = has3107Annotation self.hasTypeComment = hasTypeComment self.isDynamicallyTyped = isDynamicallyTyped @@ -84,18 +80,15 @@ annotationType = AnnotationType[annotationTypeName] newArg = cls(node.arg, node.lineno, node.col_offset, annotationType) - newArg.hasTypeAnnotation = False if node.annotation: newArg.hasTypeAnnotation = True - newArg.has3107Annotation = True + + if cls._isAnnotatedAny(node.annotation): + newArg.isDynamicallyTyped = True if node.type_comment: - newArg.hasTypeAnnotation = True newArg.hasTypeComment = True - if cls._isAnnotatedAny(node.type_comment): - newArg.isDynamicallyTyped = True - return newArg @staticmethod @@ -109,10 +102,6 @@ * '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' @@ -122,8 +111,6 @@ return True elif isinstance(argExpr, ast.Attribute) and argExpr.attr == "Any": return True - elif isinstance(argExpr, str) and argExpr.split(".", maxsplit=1)[-1] == "Any": - return True return False @@ -352,7 +339,6 @@ ) if node.returns: returnArg.hasTypeAnnotation = True - returnArg.has3107Annotation = True newFunction.isReturnAnnotated = True if Argument._isAnnotatedAny(node.returns): @@ -360,12 +346,8 @@ newFunction.args.append(returnArg) - # Type comments in-line with input arguments are handled by the - # Argument class. If a function-level type comment is present, attempt - # to parse for any missed type hints. if node.type_comment: newFunction.hasTypeComment = True - newFunction = cls.tryTypeComment(newFunction, node) # Check for the presence of non-`None` returns using the special-case # return node visitor. @@ -440,95 +422,6 @@ return node.lineno, defEndColOffset @staticmethod - def tryTypeComment(funcObj, node): - """ - Static method to infer type hints from a function-level type comment. - - If a function is type commented it is assumed to have a return - annotation, otherwise Python will fail to parse the hint. - - @param funcObj reference to the Function object - @type Function - @param node reference to the function definition node - @type ast.AsyncFunctionDef or ast.FunctionDef - @return reference to the modified Function object - @rtype Function - """ - hintTree = ast.parse(node.type_comment, "<func_type>", "func_type") - hintTree = Function._maybeInjectClassArgument(hintTree, funcObj) - - for arg, hintComment in itertools.zip_longest(funcObj.args, hintTree.argtypes): - if isinstance(hintComment, ast.Ellipsis): - continue - - if arg and hintComment: - 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 - - @staticmethod - def _maybeInjectClassArgument(hintTree, funcObj): - """ - Static method to inject `self` or `cls` args into a type comment to - align with PEP 3107-style annotations. - - Because PEP 484 does not describe a method to provide partial function- - level type comments, there is a potential for ambiguity in the context - of both class methods and classmethods when aligning type comments to - method arguments. - - These two class methods, for example, should lint equivalently: - - def bar(self, a): - # type: (int) -> int - ... - - def bar(self, a: int) -> int - ... - - When this example type comment is parsed by `ast` and then matched with - the method's arguments, it associates the `int` hint to `self` rather - than `a`, so a dummy hint needs to be provided in situations where - `self` or `class` are not hinted in the type comment in order to - achieve equivalent linting results to PEP-3107 style annotations. - - A dummy `ast.Ellipses` constant is injected if the following criteria - are met: - 1. The function node is either a class method or classmethod - 2. The number of hinted args is at least 1 less than the number - of function args - - @param hintTree parsed type hint node - @type ast.FunctionType - @param funcObj reference to the Function object - @type Function - @return reference to the hint node - @rtype ast.FunctionType - """ - if not funcObj.isClassMethod: - # Short circuit - return hintTree - - 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 - - return hintTree - - @staticmethod def getFunctionType(functionName): """ Static method to determine the function's FunctionType from its name.