src/eric7/Utilities/ModuleParser.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9364
83dea9e54ff4
diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/Utilities/ModuleParser.py
--- a/src/eric7/Utilities/ModuleParser.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/Utilities/ModuleParser.py	Wed Jul 13 14:55:47 2022 +0200
@@ -23,8 +23,15 @@
 import Utilities
 from functools import reduce
 
-__all__ = ["Module", "Class", "Function", "Attribute", "RbModule",
-           "readModule", "getTypeFromTypeName"]
+__all__ = [
+    "Module",
+    "Class",
+    "Function",
+    "Attribute",
+    "RbModule",
+    "readModule",
+    "getTypeFromTypeName",
+]
 
 TABWIDTH = 4
 
@@ -46,7 +53,7 @@
 def getTypeFromTypeName(name):
     """
     Module function to determine the module type given the module type name.
-    
+
     @param name module type name (string)
     @return module type or -1 for failure (integer)
     """
@@ -204,7 +211,8 @@
         (?: (?: if | elif ) [ \t]+ [^:]* | else [ \t]* ) :
         (?= \s* (?: async [ \t]+ )? def)
     )""",
-    re.VERBOSE | re.DOTALL | re.MULTILINE).search
+    re.VERBOSE | re.DOTALL | re.MULTILINE,
+).search
 
 _rb_getnext = re.compile(
     r"""
@@ -341,55 +349,57 @@
             end \b [^_]
         )
     )""",
-    re.VERBOSE | re.DOTALL | re.MULTILINE).search
+    re.VERBOSE | re.DOTALL | re.MULTILINE,
+).search
 
 _hashsub = re.compile(r"""^([ \t]*)#[ \t]?""", re.MULTILINE).sub
 
 _commentsub = re.compile(r"""#[^\n]*\n|#[^\n]*$""").sub
 
-_modules = {}                           # cache of modules we've seen
+_modules = {}  # cache of modules we've seen
 
 
 class VisibilityBase:
     """
     Class implementing the visibility aspect of all objects.
     """
+
     def isPrivate(self):
         """
         Public method to check, if the visibility is Private.
-        
+
         @return flag indicating Private visibility (boolean)
         """
         return self.visibility == 0
-        
+
     def isProtected(self):
         """
         Public method to check, if the visibility is Protected.
-        
+
         @return flag indicating Protected visibility (boolean)
         """
         return self.visibility == 1
-        
+
     def isPublic(self):
         """
         Public method to check, if the visibility is Public.
-        
+
         @return flag indicating Public visibility (boolean)
         """
         return self.visibility == 2
-        
+
     def setPrivate(self):
         """
         Public method to set the visibility to Private.
         """
         self.visibility = 0
-        
+
     def setProtected(self):
         """
         Public method to set the visibility to Protected.
         """
         self.visibility = 1
-        
+
     def setPublic(self):
         """
         Public method to set the visibility to Public.
@@ -401,10 +411,11 @@
     """
     Class to represent a Python module.
     """
+
     def __init__(self, name, file=None, moduleType=None):
         """
         Constructor
-        
+
         @param name name of this module (string)
         @param file filename of file containing this module (string)
         @param moduleType type of this module
@@ -421,7 +432,7 @@
         self.globals = {}
         self.imports = []
         self.from_imports = {}
-        self.package = '.'.join(name.split('.')[:-1])
+        self.package = ".".join(name.split(".")[:-1])
         self.type = moduleType
         if moduleType in [PY_SOURCE, PTL_SOURCE]:
             self._getnext = _py_getnext
@@ -429,11 +440,11 @@
             self._getnext = _rb_getnext
         else:
             self._getnext = None
-    
+
     def addClass(self, name, _class):
         """
         Public method to add information about a class.
-        
+
         @param name name of class to be added (string)
         @param _class Class object to be added
         """
@@ -443,11 +454,11 @@
         else:
             self.classes_counts[name] = 0
         self.classes[name] = _class
-    
+
     def addModule(self, name, module):
         """
         Public method to add information about a Ruby module.
-        
+
         @param name name of module to be added (string)
         @param module Module object to be added
         """
@@ -457,11 +468,11 @@
         else:
             self.modules_counts[name] = 0
         self.modules[name] = module
-    
+
     def addFunction(self, name, function):
         """
         Public method to add information about a function.
-        
+
         @param name name of function to be added (string)
         @param function Function object to be added
         """
@@ -471,11 +482,11 @@
         else:
             self.functions_counts[name] = 0
         self.functions[name] = function
-    
+
     def addGlobal(self, name, attr):
         """
         Public method to add information about global variables.
-        
+
         @param name name of the global to add (string)
         @param attr Attribute object to be added
         """
@@ -483,20 +494,20 @@
             self.globals[name] = attr
         else:
             self.globals[name].addAssignment(attr.lineno)
-    
+
     def addDescription(self, description):
         """
         Public method to store the modules docstring.
-        
+
         @param description the docstring to be stored (string)
         """
         self.description = description
-    
+
     def scan(self, src):
         """
         Public method to scan the source text and retrieve the relevant
         information.
-        
+
         @param src the source text to be scanned (string)
         """
         # convert eol markers the Python style
@@ -505,32 +516,32 @@
             self.__py_scan(src)
         elif self.type == RB_SOURCE:
             self.__rb_scan(src)
-    
+
     def __py_setVisibility(self, objectRef):
         """
         Private method to set the visibility of an object.
-        
+
         @param objectRef reference to the object (Attribute, Class or Function)
         """
-        if objectRef.name.startswith('__'):
+        if objectRef.name.startswith("__"):
             objectRef.setPrivate()
-        elif objectRef.name.startswith('_'):
+        elif objectRef.name.startswith("_"):
             objectRef.setProtected()
         else:
             objectRef.setPublic()
-    
+
     def __py_scan(self, src):
         """
         Private method to scan the source text of a Python module and retrieve
         the relevant information.
-        
+
         @param src the source text to be scanned (string)
         """
         # __IGNORE_WARNING_D234__
         def calculateEndline(lineno, lines, indent):
             """
             Function to calculate the end line of a class or method/function.
-            
+
             @param lineno line number to start at (one based)
             @type int
             @param lines list of source lines
@@ -550,12 +561,12 @@
                     if lineIndent <= indent:
                         return lineno
                 lineno += 1
-            
+
             # nothing found
             return -1
-        
+
         srcLines = src.splitlines()
-        
+
         lineno, last_lineno_pos = 1, 0
         classstack = []  # stack of (class, indent) pairs
         conditionalsstack = []  # stack of indents of conditional defines
@@ -572,30 +583,30 @@
             if not m:
                 break
             start, i = m.span()
-            
+
             if m.start("MethodModifier") >= 0:
                 modifierIndent = _indent(m.group("MethodModifierIndent"))
                 modifierType = m.group("MethodModifierType")
-            
+
             elif m.start("Method") >= 0:
                 # found a method definition or function
                 thisindent = _indent(m.group("MethodIndent"))
                 meth_name = m.group("MethodName")
                 meth_sig = m.group("MethodSignature")
-                meth_sig = meth_sig.replace('\\\n', '')
+                meth_sig = meth_sig.replace("\\\n", "")
                 meth_ret = m.group("MethodReturnAnnotation")
-                meth_ret = meth_ret.replace('\\\n', '')
+                meth_ret = meth_ret.replace("\\\n", "")
                 if m.group("MethodPyQtSignature") is not None:
                     meth_pyqtSig = (
                         m.group("MethodPyQtSignature")
-                        .replace('\\\n', '')
-                        .split('result')[0]
-                        .split('name')[0]
+                        .replace("\\\n", "")
+                        .split("result")[0]
+                        .split("name")[0]
                         .strip("\"', \t")
                     )
                 else:
                     meth_pyqtSig = None
-                lineno += src.count('\n', last_lineno_pos, start)
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 if modifierType and modifierIndent == thisindent:
                     if modifierType == "@staticmethod":
@@ -610,17 +621,12 @@
                 if conditionalsstack:
                     if thisindent > conditionalsstack[-1]:
                         if not deltaindentcalculated:
-                            deltastack.append(
-                                thisindent - conditionalsstack[-1])
-                            deltaindent = reduce(
-                                lambda x, y: x + y, deltastack)
+                            deltastack.append(thisindent - conditionalsstack[-1])
+                            deltaindent = reduce(lambda x, y: x + y, deltastack)
                             deltaindentcalculated = 1
                         thisindent -= deltaindent
                     else:
-                        while (
-                            conditionalsstack and
-                            conditionalsstack[-1] >= thisindent
-                        ):
+                        while conditionalsstack and conditionalsstack[-1] >= thisindent:
                             del conditionalsstack[-1]
                             if deltastack:
                                 del deltastack[-1]
@@ -636,81 +642,96 @@
                         csi -= 1
                         if cur_class is None:
                             continue
-                        
+
                         if isinstance(cur_class, Class):
                             # it's a class method
                             f = Function(
-                                None, meth_name, None, lineno,
-                                meth_sig, meth_pyqtSig, modifierType=modifier,
-                                annotation=meth_ret)
+                                None,
+                                meth_name,
+                                None,
+                                lineno,
+                                meth_sig,
+                                meth_pyqtSig,
+                                modifierType=modifier,
+                                annotation=meth_ret,
+                            )
                             self.__py_setVisibility(f)
                             cur_class.addMethod(meth_name, f)
                             break
                     else:
                         # it's a nested function of a module function
                         f = Function(
-                            self.name, meth_name, self.file, lineno,
-                            meth_sig, meth_pyqtSig, modifierType=modifier,
-                            annotation=meth_ret)
+                            self.name,
+                            meth_name,
+                            self.file,
+                            lineno,
+                            meth_sig,
+                            meth_pyqtSig,
+                            modifierType=modifier,
+                            annotation=meth_ret,
+                        )
                         self.__py_setVisibility(f)
                         self.addFunction(meth_name, f)
                 else:
                     # it's a module function
-                    f = Function(self.name, meth_name, self.file, lineno,
-                                 meth_sig, meth_pyqtSig, modifierType=modifier,
-                                 annotation=meth_ret)
+                    f = Function(
+                        self.name,
+                        meth_name,
+                        self.file,
+                        lineno,
+                        meth_sig,
+                        meth_pyqtSig,
+                        modifierType=modifier,
+                        annotation=meth_ret,
+                    )
                     self.__py_setVisibility(f)
                     self.addFunction(meth_name, f)
                 endlineno = calculateEndline(lineno, srcLines, thisindent)
                 f.setEndLine(endlineno)
                 cur_obj = f
                 classstack.append((None, thisindent))  # Marker for nested fns
-                
+
                 # reset the modifier settings
                 modifierType = Function.General
                 modifierIndent = -1
-            
+
             elif m.start("Docstring") >= 0:
                 contents = m.group("DocstringContents3")
                 if contents is not None:
                     contents = _hashsub(r"\1", contents)
                 else:
-                    if self.file.lower().endswith('.ptl'):
+                    if self.file.lower().endswith(".ptl"):
                         contents = ""
                     else:
-                        contents = (
-                            m.group("DocstringContents1") or
-                            m.group("DocstringContents2")
+                        contents = m.group("DocstringContents1") or m.group(
+                            "DocstringContents2"
                         )
                 if cur_obj:
                     cur_obj.addDescription(contents)
-            
+
             elif m.start("String") >= 0:
-                if (
-                    modulelevel and (
-                        src[start - len('\r\n'):start] == '\r\n' or
-                        src[start - len('\n'):start] == '\n' or
-                        src[start - len('\r'):start] == '\r'
-                    )
+                if modulelevel and (
+                    src[start - len("\r\n") : start] == "\r\n"
+                    or src[start - len("\n") : start] == "\n"
+                    or src[start - len("\r") : start] == "\r"
                 ):
                     contents = m.group("StringContents3")
                     if contents is not None:
                         contents = _hashsub(r"\1", contents)
                     else:
-                        if self.file.lower().endswith('.ptl'):
+                        if self.file.lower().endswith(".ptl"):
                             contents = ""
                         else:
-                            contents = (
-                                m.group("StringContents1") or
-                                m.group("StringContents2")
+                            contents = m.group("StringContents1") or m.group(
+                                "StringContents2"
                             )
                     if cur_obj:
                         cur_obj.addDescription(contents)
-            
+
             elif m.start("Class") >= 0:
                 # we found a class definition
                 thisindent = _indent(m.group("ClassIndent"))
-                lineno += src.count('\n', last_lineno_pos, start)
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 # close all classes indented at least as much
                 while classstack and classstack[-1][1] >= thisindent:
@@ -720,16 +741,16 @@
                 if inherit:
                     # the class inherits from other classes
                     inherit = inherit[1:-1].strip()
-                    inherit = _commentsub('', inherit)
+                    inherit = _commentsub("", inherit)
                     names = []
-                    for n in inherit.split(','):
+                    for n in inherit.split(","):
                         n = n.strip()
                         if n:
                             if n in self.classes:
                                 # we know this super class
                                 n = self.classes[n].name
                             else:
-                                c = n.split('.')
+                                c = n.split(".")
                                 if len(c) > 1:
                                     # super class is of the
                                     # form module.class:
@@ -745,26 +766,18 @@
                 if conditionalsstack:
                     if thisindent > conditionalsstack[-1]:
                         if not deltaindentcalculated:
-                            deltastack.append(
-                                thisindent - conditionalsstack[-1]
-                            )
-                            deltaindent = reduce(
-                                lambda x, y: x + y, deltastack
-                            )
+                            deltastack.append(thisindent - conditionalsstack[-1])
+                            deltaindent = reduce(lambda x, y: x + y, deltastack)
                             deltaindentcalculated = True
                         thisindent -= deltaindent
                     else:
-                        while (
-                            conditionalsstack and
-                            conditionalsstack[-1] >= thisindent
-                        ):
+                        while conditionalsstack and conditionalsstack[-1] >= thisindent:
                             del conditionalsstack[-1]
                             if deltastack:
                                 del deltastack[-1]
                         deltaindentcalculated = False
                 # remember this class
-                cur_class = Class(self.name, class_name, inherit,
-                                  self.file, lineno)
+                cur_class = Class(self.name, class_name, inherit, self.file, lineno)
                 self.__py_setVisibility(cur_class)
                 endlineno = calculateEndline(lineno, srcLines, thisindent)
                 cur_class.setEndLine(endlineno)
@@ -772,39 +785,38 @@
                 self.addClass(class_name, cur_class)
                 # add nested classes to the module
                 classstack.append((cur_class, thisindent))
-            
+
             elif m.start("Attribute") >= 0:
-                lineno += src.count('\n', last_lineno_pos, start)
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 index = -1
                 while index >= -len(classstack):
                     if classstack[index][0] is not None:
                         attrName = m.group("AttributeName")
-                        attr = Attribute(
-                            self.name, attrName, self.file, lineno)
+                        attr = Attribute(self.name, attrName, self.file, lineno)
                         self.__py_setVisibility(attr)
                         classstack[index][0].addAttribute(attrName, attr)
                         break
                     else:
                         index -= 1
-            
+
             elif m.start("Main") >= 0:
                 # 'main' part of the script, reset class stack
-                lineno += src.count('\n', last_lineno_pos, start)
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 classstack = []
-            
+
             elif m.start("Variable") >= 0:
                 thisindent = _indent(m.group("VariableIndent"))
                 variable_name = m.group("VariableName")
                 isSignal = m.group("VariableSignal") != ""
-                lineno += src.count('\n', last_lineno_pos, start)
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 if thisindent == 0:
                     # global variable
                     attr = Attribute(
-                        self.name, variable_name, self.file, lineno,
-                        isSignal=isSignal)
+                        self.name, variable_name, self.file, lineno, isSignal=isSignal
+                    )
                     self.__py_setVisibility(attr)
                     self.addGlobal(variable_name, attr)
                 else:
@@ -813,74 +825,77 @@
                         if classstack[index][1] >= thisindent:
                             index -= 1
                         else:
-                            if (
-                                classstack[index][0] is not None and
-                                isinstance(classstack[index][0], Class)
+                            if classstack[index][0] is not None and isinstance(
+                                classstack[index][0], Class
                             ):
                                 attr = Attribute(
-                                    self.name, variable_name, self.file,
-                                    lineno, isSignal=isSignal)
+                                    self.name,
+                                    variable_name,
+                                    self.file,
+                                    lineno,
+                                    isSignal=isSignal,
+                                )
                                 self.__py_setVisibility(attr)
-                                classstack[index][0].addGlobal(
-                                    variable_name, attr)
+                                classstack[index][0].addGlobal(variable_name, attr)
                             break
 
             elif m.start("Import") >= 0:
-                #- import module
-                names = [n.strip() for n in
-                         "".join(m.group("ImportList").splitlines())
-                         .replace("\\", "").split(',')]
+                # - import module
+                names = [
+                    n.strip()
+                    for n in "".join(m.group("ImportList").splitlines())
+                    .replace("\\", "")
+                    .split(",")
+                ]
                 self.imports.extend(
-                    [name for name in names
-                     if name not in self.imports])
-            
+                    [name for name in names if name not in self.imports]
+                )
+
             elif m.start("ImportFrom") >= 0:
-                #- from module import stuff
+                # - from module import stuff
                 mod = m.group("ImportFromPath")
-                namesLines = (m.group("ImportFromList")
-                              .replace("(", "").replace(")", "")
-                              .replace("\\", "")
-                              .strip().splitlines())
-                namesLines = [line.split("#")[0].strip()
-                              for line in namesLines]
-                names = [n.strip() for n in
-                         "".join(namesLines)
-                         .split(',')]
+                namesLines = (
+                    m.group("ImportFromList")
+                    .replace("(", "")
+                    .replace(")", "")
+                    .replace("\\", "")
+                    .strip()
+                    .splitlines()
+                )
+                namesLines = [line.split("#")[0].strip() for line in namesLines]
+                names = [n.strip() for n in "".join(namesLines).split(",")]
                 if mod not in self.from_imports:
                     self.from_imports[mod] = []
                 self.from_imports[mod].extend(
-                    [name for name in names
-                     if name not in self.from_imports[mod]])
-            
+                    [name for name in names if name not in self.from_imports[mod]]
+                )
+
             elif m.start("ConditionalDefine") >= 0:
                 # a conditional function/method definition
                 thisindent = _indent(m.group("ConditionalDefineIndent"))
-                while (
-                    conditionalsstack and
-                    conditionalsstack[-1] >= thisindent
-                ):
+                while conditionalsstack and conditionalsstack[-1] >= thisindent:
                     del conditionalsstack[-1]
                     if deltastack:
                         del deltastack[-1]
                 conditionalsstack.append(thisindent)
                 deltaindentcalculated = 0
-            
+
             elif m.start("Comment") >= 0 and modulelevel:
                 continue
-            
+
             modulelevel = False
-    
+
     def __rb_scan(self, src):
         """
         Private method to scan the source text of a Python module and retrieve
         the relevant information.
-        
+
         @param src the source text to be scanned
         @type str
         """
         lineno, last_lineno_pos = 1, 0
         classstack = []  # stack of (class, indent) pairs
-        acstack = []    # stack of (access control, indent) pairs
+        acstack = []  # stack of (access control, indent) pairs
         indent = 0
         i = 0
         cur_obj = self
@@ -890,30 +905,28 @@
             if not m:
                 break
             start, i = m.span()
-            
+
             if m.start("Method") >= 0:
                 # found a method definition or function
                 thisindent = indent
                 indent += 1
                 meth_name = (
-                    m.group("MethodName") or
-                    m.group("MethodName2") or
-                    m.group("MethodName3")
+                    m.group("MethodName")
+                    or m.group("MethodName2")
+                    or m.group("MethodName3")
                 )
                 meth_sig = m.group("MethodSignature")
-                meth_sig = meth_sig and meth_sig.replace('\\\n', '') or ''
-                lineno += src.count('\n', last_lineno_pos, start)
+                meth_sig = meth_sig and meth_sig.replace("\\\n", "") or ""
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
-                if meth_name.startswith('self.'):
+                if meth_name.startswith("self."):
                     meth_name = meth_name[5:]
-                elif meth_name.startswith('self::'):
+                elif meth_name.startswith("self::"):
                     meth_name = meth_name[6:]
                 # close all classes/modules indented at least as much
                 while classstack and classstack[-1][1] >= thisindent:
-                    if (
-                        classstack[-1][0] is not None and
-                        isinstance(classstack[-1][0],
-                                   (Class, Function, RbModule))
+                    if classstack[-1][0] is not None and isinstance(
+                        classstack[-1][0], (Class, Function, RbModule)
                     ):
                         # record the end line of this class, function or module
                         classstack[-1][0].setEndLine(lineno - 1)
@@ -928,17 +941,15 @@
                         csi -= 1
                         if cur_class is None:
                             continue
-                        
+
                         if isinstance(cur_class, (Class, RbModule)):
                             # it's a class/module method
-                            f = Function(None, meth_name,
-                                         None, lineno, meth_sig)
+                            f = Function(None, meth_name, None, lineno, meth_sig)
                             cur_class.addMethod(meth_name, f)
                             break
                     else:
                         # it's a nested function of a module function
-                        f = Function(
-                            self.name, meth_name, self.file, lineno, meth_sig)
+                        f = Function(self.name, meth_name, self.file, lineno, meth_sig)
                         self.addFunction(meth_name, f)
                     # set access control
                     if acstack:
@@ -951,8 +962,7 @@
                             f.setPublic()
                 else:
                     # it's a function
-                    f = Function(
-                        self.name, meth_name, self.file, lineno, meth_sig)
+                    f = Function(self.name, meth_name, self.file, lineno, meth_sig)
                     self.addFunction(meth_name, f)
                 if not classstack:
                     if lastGlobalEntry:
@@ -962,26 +972,24 @@
                     cur_obj.setEndLine(lineno - 1)
                 cur_obj = f
                 classstack.append((None, thisindent))  # Marker for nested fns
-            
+
             elif m.start("Docstring") >= 0:
                 contents = m.group("DocstringContents")
                 if contents is not None:
                     contents = _hashsub(r"\1", contents)
                 if cur_obj:
                     cur_obj.addDescription(contents)
-            
+
             elif m.start("Class") >= 0:
                 # we found a class definition
                 thisindent = indent
                 indent += 1
-                lineno += src.count('\n', last_lineno_pos, start)
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 # close all classes/modules indented at least as much
                 while classstack and classstack[-1][1] >= thisindent:
-                    if (
-                        classstack[-1][0] is not None and
-                        isinstance(classstack[-1][0],
-                                   (Class, Function, RbModule))
+                    if classstack[-1][0] is not None and isinstance(
+                        classstack[-1][0], (Class, Function, RbModule)
                     ):
                         # record the end line of this class, function or module
                         classstack[-1][0].setEndLine(lineno - 1)
@@ -991,10 +999,9 @@
                 if inherit:
                     # the class inherits from other classes
                     inherit = inherit[1:].strip()
-                    inherit = [_commentsub('', inherit)]
+                    inherit = [_commentsub("", inherit)]
                 # remember this class
-                cur_class = Class(self.name, class_name, inherit,
-                                  self.file, lineno)
+                cur_class = Class(self.name, class_name, inherit, self.file, lineno)
                 # add nested classes to the file
                 if classstack and isinstance(classstack[-1][0], RbModule):
                     parent_obj = classstack[-1][0]
@@ -1003,9 +1010,9 @@
                 if class_name in parent_obj.classes:
                     cur_class = parent_obj.classes[class_name]
                 elif (
-                    classstack and
-                    isinstance(classstack[-1][0], Class) and
-                    class_name == "self"
+                    classstack
+                    and isinstance(classstack[-1][0], Class)
+                    and class_name == "self"
                 ):
                     cur_class = classstack[-1][0]
                 else:
@@ -1020,27 +1027,24 @@
                     del acstack[-1]
                 acstack.append(["public", thisindent])
                 # default access control is 'public'
-            
+
             elif m.start("Module") >= 0:
                 # we found a module definition
                 thisindent = indent
                 indent += 1
-                lineno += src.count('\n', last_lineno_pos, start)
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 # close all classes/modules indented at least as much
                 while classstack and classstack[-1][1] >= thisindent:
-                    if (
-                        classstack[-1][0] is not None and
-                        isinstance(classstack[-1][0],
-                                   (Class, Function, RbModule))
+                    if classstack[-1][0] is not None and isinstance(
+                        classstack[-1][0], (Class, Function, RbModule)
                     ):
                         # record the end line of this class, function or module
                         classstack[-1][0].setEndLine(lineno - 1)
                     del classstack[-1]
                 module_name = m.group("ModuleName")
                 # remember this class
-                cur_class = RbModule(self.name, module_name,
-                                     self.file, lineno)
+                cur_class = RbModule(self.name, module_name, self.file, lineno)
                 # add nested Ruby modules to the file
                 if module_name in self.modules:
                     cur_class = self.modules[module_name]
@@ -1056,7 +1060,7 @@
                     del acstack[-1]
                 acstack.append(["public", thisindent])
                 # default access control is 'public'
-            
+
             elif m.start("AccessControl") >= 0:
                 aclist = m.group("AccessControlList")
                 if aclist is None:
@@ -1064,8 +1068,8 @@
                     while index >= -len(acstack):
                         if acstack[index][1] < indent:
                             actype = (
-                                m.group("AccessControlType") or
-                                m.group("AccessControlType2").split('_')[0]
+                                m.group("AccessControlType")
+                                or m.group("AccessControlType2").split("_")[0]
                             )
                             acstack[index][0] = actype.lower()
                             break
@@ -1075,14 +1079,14 @@
                     index = -1
                     while index >= -len(classstack):
                         if (
-                            classstack[index][0] is not None and
-                            not isinstance(classstack[index][0], Function) and
-                            classstack[index][1] < indent
+                            classstack[index][0] is not None
+                            and not isinstance(classstack[index][0], Function)
+                            and classstack[index][1] < indent
                         ):
                             parent = classstack[index][0]
                             actype = (
-                                m.group("AccessControlType") or
-                                m.group("AccessControlType2").split('_')[0]
+                                m.group("AccessControlType")
+                                or m.group("AccessControlType2").split("_")[0]
                             )
                             actype = actype.lower()
                             for name in aclist.split(","):
@@ -1102,18 +1106,17 @@
                             index -= 1
 
             elif m.start("Attribute") >= 0:
-                lineno += src.count('\n', last_lineno_pos, start)
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 index = -1
                 while index >= -len(classstack):
                     if (
-                        classstack[index][0] is not None and
-                        not isinstance(classstack[index][0], Function) and
-                        classstack[index][1] < indent
+                        classstack[index][0] is not None
+                        and not isinstance(classstack[index][0], Function)
+                        and classstack[index][1] < indent
                     ):
                         attrName = m.group("AttributeName")
-                        attr = Attribute(
-                            self.name, attrName, self.file, lineno)
+                        attr = Attribute(self.name, attrName, self.file, lineno)
                         if attrName.startswith("@@") or attrName[0].isupper():
                             classstack[index][0].addGlobal(attrName, attr)
                         else:
@@ -1124,22 +1127,21 @@
                 else:
                     attrName = m.group("AttributeName")
                     if attrName[0] != "@":
-                        attr = Attribute(
-                            self.name, attrName, self.file, lineno)
+                        attr = Attribute(self.name, attrName, self.file, lineno)
                         self.addGlobal(attrName, attr)
                     if lastGlobalEntry:
                         lastGlobalEntry.setEndLine(lineno - 1)
                     lastGlobalEntry = None
-            
+
             elif m.start("Attr") >= 0:
-                lineno += src.count('\n', last_lineno_pos, start)
+                lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 index = -1
                 while index >= -len(classstack):
                     if (
-                        classstack[index][0] is not None and
-                        not isinstance(classstack[index][0], Function) and
-                        classstack[index][1] < indent
+                        classstack[index][0] is not None
+                        and not isinstance(classstack[index][0], Function)
+                        and classstack[index][1] < indent
                     ):
                         parent = classstack[index][0]
                         if m.group("AttrType") is None:
@@ -1149,10 +1151,9 @@
                             # get rid of leading ':'
                             name = nv[0].strip()[1:]
                             attr = (
-                                parent.getAttribute("@" + name) or
-                                parent.getAttribute("@@" + name) or
-                                Attribute(
-                                    self.name, "@" + name, self.file, lineno)
+                                parent.getAttribute("@" + name)
+                                or parent.getAttribute("@@" + name)
+                                or Attribute(self.name, "@" + name, self.file, lineno)
                             )
                             if len(nv) == 1 or nv[1].strip() == "false":
                                 attr.setProtected()
@@ -1165,11 +1166,11 @@
                                 # get rid of leading ':'
                                 name = name.strip()[1:]
                                 attr = (
-                                    parent.getAttribute("@" + name) or
-                                    parent.getAttribute("@@" + name) or
-                                    Attribute(
-                                        self.name, "@" + name, self.file,
-                                        lineno)
+                                    parent.getAttribute("@" + name)
+                                    or parent.getAttribute("@@" + name)
+                                    or Attribute(
+                                        self.name, "@" + name, self.file, lineno
+                                    )
                                 )
                                 if access == "_accessor":
                                     attr.setPublic()
@@ -1186,7 +1187,7 @@
             elif m.start("Begin") >= 0:
                 # a begin of a block we are not interested in
                 indent += 1
-            
+
             elif m.start("End") >= 0:
                 # an end of a block
                 indent -= 1
@@ -1197,20 +1198,20 @@
                         indent = classstack[-1][1]
                     else:
                         indent = 0
-            
+
             elif (
-                m.start("String") >= 0 or
-                m.start("Comment") >= 0 or
-                m.start("ClassIgnored") >= 0 or
-                m.start("BeginEnd") >= 0
+                m.start("String") >= 0
+                or m.start("Comment") >= 0
+                or m.start("ClassIgnored") >= 0
+                or m.start("BeginEnd") >= 0
             ):
                 pass
-    
+
     def createHierarchy(self):
         """
         Public method to build the inheritance hierarchy for all classes of
         this module.
-        
+
         @return A dictionary with inheritance hierarchies.
         """
         hierarchy = {}
@@ -1219,17 +1220,17 @@
         for module in self.modules:
             self.assembleHierarchy(module, self.modules, [module], hierarchy)
         return hierarchy
-    
+
     def assembleHierarchy(self, name, classes, path, result):
         """
         Public method to assemble the inheritance hierarchy.
-        
+
         This method will traverse the class hierarchy, from a given class
         and build up a nested dictionary of super-classes. The result is
         intended to be inverted, i.e. the highest level are the super classes.
-        
+
         This code is borrowed from Boa Constructor.
-        
+
         @param name name of class to assemble hierarchy (string)
         @param classes A dictionary of classes to look in.
         @param path
@@ -1242,21 +1243,21 @@
                     rv[class_] = {}
                     exhausted = path + [class_]
                     exhausted.reverse()
-                    self.addPathToHierarchy(
-                        exhausted, result, self.addPathToHierarchy)
+                    self.addPathToHierarchy(exhausted, result, self.addPathToHierarchy)
                 else:
                     rv[class_] = self.assembleHierarchy(
-                        class_, classes, path + [class_], result)
-        
+                        class_, classes, path + [class_], result
+                    )
+
         if len(rv) == 0:
             exhausted = path
             exhausted.reverse()
             self.addPathToHierarchy(exhausted, result, self.addPathToHierarchy)
-    
+
     def addPathToHierarchy(self, path, result, fn):
         """
         Public method to put the exhausted path into the result dictionary.
-        
+
         @param path the exhausted path of classes
         @param result the result dictionary
         @param fn function to call for classe that are already part of the
@@ -1269,27 +1270,27 @@
             for part in path:
                 result[part] = {}
                 result = result[part]
-    
+
     def getName(self):
         """
         Public method to retrieve the modules name.
-        
+
         @return module name (string)
         """
         return self.name
-    
+
     def getFileName(self):
         """
         Public method to retrieve the modules filename.
-        
+
         @return module filename (string)
         """
         return self.file
-    
+
     def getType(self):
         """
         Public method to get the type of the module's source.
-        
+
         @return type of the modules's source (string)
         """
         if self.type in [PY_SOURCE, PTL_SOURCE]:
@@ -1305,10 +1306,11 @@
     """
     Class to represent a Python class.
     """
+
     def __init__(self, module, name, superClasses, file, lineno):
         """
         Constructor
-        
+
         @param module name of module containing this class (string)
         @param name name of the class (string)
         @param superClasses list of classnames this class is inherited from
@@ -1326,23 +1328,23 @@
         self.globals = {}
         self.file = file
         self.lineno = lineno
-        self.endlineno = -1     # marker for "not set"
+        self.endlineno = -1  # marker for "not set"
         self.description = ""
         self.setPublic()
 
     def addMethod(self, name, function):
         """
         Public method to add information about a method.
-        
+
         @param name name of method to be added (string)
         @param function Function object to be added
         """
         self.methods[name] = function
-    
+
     def getMethod(self, name):
         """
         Public method to retrieve a method by name.
-        
+
         @param name name of the method (string)
         @return the named method or None
         """
@@ -1350,11 +1352,11 @@
             return self.methods[name]
         except KeyError:
             return None
-    
+
     def addAttribute(self, name, attr):
         """
         Public method to add information about attributes.
-        
+
         @param name name of the attribute to add (string)
         @param attr Attribute object to be added
         """
@@ -1362,11 +1364,11 @@
             self.attributes[name] = attr
         else:
             self.attributes[name].addAssignment(attr.lineno)
-    
+
     def getAttribute(self, name):
         """
         Public method to retrieve an attribute by name.
-        
+
         @param name name of the attribute (string)
         @return the named attribute or None
         """
@@ -1374,11 +1376,11 @@
             return self.attributes[name]
         except KeyError:
             return None
-    
+
     def addGlobal(self, name, attr):
         """
         Public method to add information about global (class) variables.
-        
+
         @param name name of the global to add (string)
         @param attr Attribute object to be added
         """
@@ -1386,19 +1388,19 @@
             self.globals[name] = attr
         else:
             self.globals[name].addAssignment(attr.lineno)
-    
+
     def addDescription(self, description):
         """
         Public method to store the class docstring.
-        
+
         @param description the docstring to be stored (string)
         """
         self.description = description
-    
+
     def setEndLine(self, endLineNo):
         """
         Public method to record the number of the last line of a class.
-        
+
         @param endLineNo number of the last line (integer)
         """
         self.endlineno = endLineNo
@@ -1408,10 +1410,11 @@
     """
     Class to represent a Ruby module.
     """
+
     def __init__(self, module, name, file, lineno):
         """
         Constructor
-        
+
         @param module name of module containing this class (string)
         @param name name of the class (string)
         @param file name of file containing this class (string)
@@ -1419,11 +1422,11 @@
         """
         Class.__init__(self, module, name, None, file, lineno)
         self.classes = {}
-    
+
     def addClass(self, name, _class):
         """
         Public method to add information about a class.
-        
+
         @param name name of class to be added (string)
         @param _class Class object to be added
         """
@@ -1434,15 +1437,25 @@
     """
     Class to represent a Python function or method.
     """
+
     General = 0
     Static = 1
     Class = 2
-    
-    def __init__(self, module, name, file, lineno, signature='',
-                 pyqtSignature=None, modifierType=General, annotation=""):
+
+    def __init__(
+        self,
+        module,
+        name,
+        file,
+        lineno,
+        signature="",
+        pyqtSignature=None,
+        modifierType=General,
+        annotation="",
+    ):
         """
         Constructor
-        
+
         @param module name of module containing this function (string)
         @param name name of the function (string)
         @param file name of file containing this function (string)
@@ -1456,27 +1469,27 @@
         self.name = name
         self.file = file
         self.lineno = lineno
-        self.endlineno = -1     # marker for "not set"
-        signature = _commentsub('', signature)
-        self.parameters = [e.strip() for e in signature.split(',')]
+        self.endlineno = -1  # marker for "not set"
+        signature = _commentsub("", signature)
+        self.parameters = [e.strip() for e in signature.split(",")]
         self.description = ""
         self.pyqtSignature = pyqtSignature
         self.modifier = modifierType
         self.annotation = annotation
         self.setPublic()
-    
+
     def addDescription(self, description):
         """
         Public method to store the functions docstring.
-        
+
         @param description the docstring to be stored (string)
         """
         self.description = description
-    
+
     def setEndLine(self, endLineNo):
         """
         Public method to record the number of the last line of a class.
-        
+
         @param endLineNo number of the last line (integer)
         """
         self.endlineno = endLineNo
@@ -1486,10 +1499,11 @@
     """
     Class to represent a Python function or method.
     """
+
     def __init__(self, module, name, file, lineno, isSignal=False):
         """
         Constructor
-        
+
         @param module name of module containing this function (string)
         @param name name of the function (string)
         @param file name of file containing this function (string)
@@ -1503,11 +1517,11 @@
         self.isSignal = isSignal
         self.setPublic()
         self.linenos = [lineno]
-    
+
     def addAssignment(self, lineno):
         """
         Public method to add another assignment line number.
-        
+
         @param lineno linenumber of the additional attribute assignment
             (integer)
         """
@@ -1515,15 +1529,22 @@
             self.linenos.append(lineno)
 
 
-def readModule(module, path=None, inpackage=False, basename="",
-               extensions=None, caching=True, ignoreBuiltinModules=False):
+def readModule(
+    module,
+    path=None,
+    inpackage=False,
+    basename="",
+    extensions=None,
+    caching=True,
+    ignoreBuiltinModules=False,
+):
     """
     Function to read a module file and parse it.
 
     The module is searched in path and sys.path, read and parsed.
     If the module was parsed before, the information is taken
     from a cache in order to speed up processing.
-    
+
     @param module name of the module to be parsed (string)
     @param path search path for the module (list of strings)
     @param inpackage flag indicating that module is inside a
@@ -1540,73 +1561,69 @@
         module information (Module)
     """
     global _modules
-    
+
     _extensions = (
-        ['.py', '.pyw', '.ptl', '.rb']
-        if extensions is None else
-        extensions[:]
+        [".py", ".pyw", ".ptl", ".rb"] if extensions is None else extensions[:]
     )
     with contextlib.suppress(ValueError):
-        _extensions.remove('.py')
-    
+        _extensions.remove(".py")
+
     modname = module
-    
+
     if os.path.exists(module):
         path = [os.path.dirname(module)]
         if module.lower().endswith(".py"):
             module = module[:-3]
         if (
-            os.path.exists(os.path.join(path[0], "__init__.py")) or
-            os.path.exists(os.path.join(path[0], "__init__.rb")) or
-            inpackage
+            os.path.exists(os.path.join(path[0], "__init__.py"))
+            or os.path.exists(os.path.join(path[0], "__init__.rb"))
+            or inpackage
         ):
             if basename:
                 module = module.replace(basename, "")
             if os.path.isabs(module):
-                modname = os.path.splitdrive(module)[1][len(os.sep):]
+                modname = os.path.splitdrive(module)[1][len(os.sep) :]
             else:
                 modname = module
-            modname = modname.replace(os.sep, '.')
+            modname = modname.replace(os.sep, ".")
             inpackage = 1
         else:
             modname = os.path.basename(module)
         for ext in _extensions:
             if modname.lower().endswith(ext):
-                modname = modname[:-len(ext)]
+                modname = modname[: -len(ext)]
                 break
         module = os.path.basename(module)
-    
+
     if caching and modname in _modules:
         # we've seen this module before...
         return _modules[modname]
-    
+
     if not ignoreBuiltinModules and module in sys.builtin_module_names:
         # this is a built-in module
         mod = Module(modname, None, None)
         if caching:
             _modules[modname] = mod
         return mod
-    
+
     # search the path for the module
     path = [] if path is None else path[:]
     f = None
     if inpackage:
         try:
-            f, file, (suff, mode, moduleType) = find_module(
-                module, path, _extensions)
+            f, file, (suff, mode, moduleType) = find_module(module, path, _extensions)
         except ImportError:
             f = None
     if f is None:
         fullpath = path[:] + sys.path[:]
-        f, file, (suff, mode, moduleType) = find_module(
-            module, fullpath, _extensions)
+        f, file, (suff, mode, moduleType) = find_module(module, fullpath, _extensions)
     if f:
         f.close()
     if moduleType not in SUPPORTED_TYPES:
         # not supported source, can't do anything with this module
         _modules[modname] = Module(modname, None, None)
         return _modules[modname]
-    
+
     mod = Module(modname, file, moduleType)
     with contextlib.suppress(UnicodeError, OSError):
         src = Utilities.readEncodedFile(file)[0]
@@ -1619,7 +1636,7 @@
 def _indent(ws):
     """
     Protected function to determine the indent width of a whitespace string.
-    
+
     @param ws The whitespace string to be cheked. (string)
     @return Length of the whitespace string after tab expansion.
     """
@@ -1629,12 +1646,12 @@
 def find_module(name, path, extensions):
     """
     Module function to extend the Python module finding mechanism.
-    
+
     This function searches for files in the given path. If the filename
     doesn't have an extension or an extension of .py, the normal search
     implemented in the imp module is used. For all other supported files
     only path is searched.
-    
+
     @param name filename or modulename to search for (string)
     @param path search path (list of strings)
     @param extensions list of extensions, which should be considered valid
@@ -1645,43 +1662,43 @@
     """
     for ext in extensions:
         if name.lower().endswith(ext):
-            for p in path:      # only search in path
+            for p in path:  # only search in path
                 if os.path.exists(os.path.join(p, name)):
                     pathname = os.path.join(p, name)
-                    if ext == '.ptl':
+                    if ext == ".ptl":
                         # Quixote page template
                         return (
-                            open(pathname), pathname,
-                            # __IGNORE_WARNING_Y115__
-                            ('.ptl', 'r', PTL_SOURCE)
+                            open(pathname),  # __IGNORE_WARNING_Y115__
+                            pathname,
+                            (".ptl", "r", PTL_SOURCE),
                         )
-                    elif ext == '.rb':
+                    elif ext == ".rb":
                         # Ruby source file
                         return (
-                            open(pathname), pathname,
-                            # __IGNORE_WARNING_Y115__
-                            ('.rb', 'r', RB_SOURCE)
+                            open(pathname),  # __IGNORE_WARNING_Y115__
+                            pathname,
+                            (".rb", "r", RB_SOURCE),
                         )
                     else:
                         return (
-                            open(pathname), pathname,
-                            # __IGNORE_WARNING_Y115__
-                            (ext, 'r', PY_SOURCE)
+                            open(pathname),  # __IGNORE_WARNING_Y115__
+                            pathname,
+                            (ext, "r", PY_SOURCE),
                         )
             raise ImportError
-    
+
     # standard Python module file
-    if name.lower().endswith('.py'):
+    if name.lower().endswith(".py"):
         name = name[:-3]
-    
+
     spec = importlib.machinery.PathFinder.find_spec(name, path)
     if spec is None:
         raise ImportError
     if isinstance(spec.loader, importlib.machinery.SourceFileLoader):
         ext = os.path.splitext(spec.origin)[-1]
-        return (open(spec.origin), spec.origin, (ext, 'r', PY_SOURCE))
+        return (open(spec.origin), spec.origin, (ext, "r", PY_SOURCE))
         # __IGNORE_WARNING_Y115__
-    
+
     raise ImportError
 
 
@@ -1690,18 +1707,18 @@
     Module function to reset the list of modules already parsed.
     """
     _modules.clear()
-    
+
 
 def resetParsedModule(module, basename=""):
     """
     Module function to clear one module from the list of parsed modules.
-    
+
     @param module Name of the module to be parsed (string)
     @param basename a path basename. This basename is deleted from
         the filename of the module file to be cleared. (string)
     """
     modname = module
-    
+
     if os.path.exists(module):
         path = [os.path.dirname(module)]
         if module.lower().endswith(".py"):
@@ -1709,17 +1726,14 @@
         if os.path.exists(os.path.join(path[0], "__init__.py")):
             if basename:
                 module = module.replace(basename, "")
-            modname = module.replace(os.sep, '.')
+            modname = module.replace(os.sep, ".")
         else:
             modname = os.path.basename(module)
-        if (
-            modname.lower().endswith(".ptl") or
-            modname.lower().endswith(".pyw")
-        ):
+        if modname.lower().endswith(".ptl") or modname.lower().endswith(".pyw"):
             modname = modname[:-4]
         elif modname.lower().endswith(".rb"):
             modname = modname[:-3]
         module = os.path.basename(module)
-    
+
     if modname in _modules:
         del _modules[modname]

eric ide

mercurial