eric7/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsFunctionVisitor.py

branch
eric7
changeset 8773
3dd81b827455
parent 8312
800c432b34c8
child 8881
54e42bc2437a
--- a/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsFunctionVisitor.py	Tue Nov 16 18:00:40 2021 +0100
+++ b/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsFunctionVisitor.py	Thu Nov 18 19:37:14 2021 +0100
@@ -8,11 +8,12 @@
 """
 
 #
-# The visitor and associated classes are adapted from flake8-annotations v2.6.2
+# The visitor and associated classes are adapted from flake8-annotations v2.7.0
 #
 
 import ast
 import itertools
+import sys
 
 from .AnnotationsEnums import AnnotationType, ClassDecoratorType, FunctionType
 
@@ -273,10 +274,12 @@
         # Store raw decorator list for use by property methods
         kwargs["decoratorList"] = node.decorator_list
         
+        # Instantiate empty args list here since it has no default
+        kwargs["args"] = []
+
         newFunction = cls(node.name, node.lineno, node.col_offset, **kwargs)
         
         # Iterate over arguments by type & add
-        newFunction.args = []
         for argType in AST_ARG_TYPES:
             args = node.args.__getattribute__(argType)
             if args:
@@ -332,7 +335,27 @@
             return Function._singleLineColonSeeker(
                 node, lines[node.lineno - 1])
         
-        defEndLineno = node.body[0].lineno - 1
+        # With Python < 3.8, the function node includes the docstring and the
+        # body does not, so we have to rewind through any docstrings, if
+        # present, before looking for the def colon. We should end up with
+        # lines[defEndLineno - 1] having the colon.
+        defEndLineno = node.body[0].lineno
+        if sys.version_info < (3, 8, 0):
+            # If the docstring is on one line then no rewinding is necessary.
+            nTripleQuotes = lines[defEndLineno - 1].count('"""')
+            if nTripleQuotes == 1:
+                # Docstring closure, rewind until the opening is found and take
+                # the line prior.
+                while True:
+                    defEndLineno -= 1
+                    if '"""' in lines[defEndLineno - 1]:
+                        # Docstring has closed
+                        break
+        
+        # Once we've gotten here, we've found the line where the docstring
+        # begins, so we have to step up one more line to get to the close of
+        # the def.
+        defEndLineno -= 1
         
         # Use str.rfind() to account for annotations on the same line,
         # definition closure should be the last : on the line
@@ -509,6 +532,8 @@
     """
     Class implementing a node visitor to check function annotations.
     """
+    AstFuncTypes = (ast.FunctionDef, ast.AsyncFunctionDef)
+    
     def __init__(self, lines):
         """
         Constructor
@@ -532,7 +557,7 @@
         @param node reference to the function definition node to be analyzed
         @type ast.AsyncFunctionDef or ast.FunctionDef
         """
-        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
+        if isinstance(node, FunctionVisitor.AstFuncTypes):
             # Check for non-empty context first to prevent IndexErrors for
             # non-nested nodes
             if self.__context:
@@ -543,8 +568,7 @@
                         Function.fromNode(node, self.lines, isClassMethod=True)
                     )
                 elif isinstance(
-                    self.__context[-1],
-                    (ast.FunctionDef, ast.AsyncFunctionDef)
+                    self.__context[-1], FunctionVisitor.AstFuncTypes
                 ):
                     # Check for nested function & pass the appropriate flag
                     self.functionDefinitions.append(

eric ide

mercurial