AssistantEric/APIsManager.py

branch
eric7
changeset 190
3104a5a3ea13
parent 186
c228779ea15d
child 191
6798a98189da
diff -r c0d638327085 -r 3104a5a3ea13 AssistantEric/APIsManager.py
--- a/AssistantEric/APIsManager.py	Thu Dec 30 11:32:05 2021 +0100
+++ b/AssistantEric/APIsManager.py	Wed Sep 21 16:59:53 2022 +0200
@@ -10,9 +10,8 @@
 import contextlib
 import os
 
-from PyQt6.QtCore import (
-    QTimer, QThread, QFileInfo, pyqtSignal, QDateTime, QObject, Qt
-)
+from PyQt6.QtCore import QTimer, QThread, QFileInfo, pyqtSignal, QDateTime, QObject, Qt
+
 with contextlib.suppress(ImportError):
     from PyQt6.QtSql import QSqlDatabase, QSqlQuery
 
@@ -36,12 +35,13 @@
 class DbAPIsWorker(QThread):
     """
     Class implementing a worker thread to prepare the API database.
-    
+
     @signal processing(status, file) emitted to indicate the processing status
             (one of WorkerStatus..., string)
     """
+
     processing = pyqtSignal(int, str)
-    
+
     populate_api_stmt = """
         INSERT INTO api (
         acWord, context, fullContext, signature, fileId, pictureId)
@@ -78,12 +78,20 @@
     api_files_stmt = """
         SELECT file FROM file
     """
-    
-    def __init__(self, proxy, language, apiFiles, dbName, projectPath="",
-                 refresh=False, projectType=""):
+
+    def __init__(
+        self,
+        proxy,
+        language,
+        apiFiles,
+        dbName,
+        projectPath="",
+        refresh=False,
+        projectType="",
+    ):
         """
         Constructor
-        
+
         @param proxy reference to the object that is proxied
         @type DbAPIs
         @param language language of the APIs object
@@ -101,9 +109,9 @@
         @type str
         """
         QThread.__init__(self)
-        
+
         self.setTerminationEnabled(True)
-        
+
         # Get the AC word separators for all of the languages that the editor
         # supports. This has to be before we create a new thread, because
         # access to GUI elements is not allowed from non-GUI threads.
@@ -112,7 +120,7 @@
             lexer = QScintilla.Lexers.getLexer(lang)
             if lexer is not None:
                 self.__wseps[lang] = lexer.autoCompletionWordSeparators()
-        
+
         self.__proxy = proxy
         self.__language = language
         self.__projectType = projectType
@@ -120,20 +128,20 @@
         self.__aborted = False
         self.__projectPath = projectPath
         self.__refresh = refresh
-        
+
         self.__databaseName = dbName
-        
+
         if self.__projectType:
             self.__connectionName = "{0}_{1}_{2}".format(
-                self.__language, self.__projectType, id(self))
+                self.__language, self.__projectType, id(self)
+            )
         else:
-            self.__connectionName = "{0}_{1}".format(
-                self.__language, id(self))
-    
+            self.__connectionName = "{0}_{1}".format(self.__language, id(self))
+
     def __autoCompletionWordSeparators(self, language):
         """
         Private method to get the word separator characters for a language.
-        
+
         @param language language of the APIs object
         @type str
         @return word separator characters
@@ -143,18 +151,18 @@
             return self.__wseps.get(language, None)
         else:
             return self.__proxy.autoCompletionWordSeparators()
-    
+
     def abort(self):
         """
         Public method to ask the thread to stop.
         """
         self.__aborted = True
-    
+
     def __loadApiFileIfNewer(self, apiFile):
         """
         Private method to load an API file, if it is newer than the one read
         into the database.
-        
+
         @param apiFile filename of the raw API file
         @type str
         """
@@ -167,7 +175,8 @@
             query.exec()
             loadTime = (
                 QDateTime.fromString(query.value(0), Qt.DateFormat.ISODate)
-                if query.next() and query.isValid() else
+                if query.next() and query.isValid()
+                else
                 # __IGNORE_WARNING_M513__
                 QDateTime(1970, 1, 1, 0, 0)
             )
@@ -176,10 +185,9 @@
         finally:
             db.commit()
         if self.__projectPath:
-            modTime = (
-                QFileInfo(os.path.join(self.__projectPath, apiFile))
-                .lastModified()
-            )
+            modTime = QFileInfo(
+                os.path.join(self.__projectPath, apiFile)
+            ).lastModified()
         else:
             modTime = QFileInfo(apiFile).lastModified()
             basesFile = os.path.splitext(apiFile)[0] + ".bas"
@@ -190,93 +198,94 @@
         if loadTime < modTime:
             self.processing.emit(WorkerStatusFile, apiFile)
             self.__loadApiFile(apiFile)
-    
+
     def __classesAttributesApi(self, module):
         """
         Private method to generate class api section for class attributes.
-        
+
         @param module module object to get the info from
         @type Module
         @return API information
         @rtype list of str
         """
         api = []
-        modulePath = module.name.split('.')
-        moduleName = "{0}.".format('.'.join(modulePath))
-        
+        modulePath = module.name.split(".")
+        moduleName = "{0}.".format(".".join(modulePath))
+
         for className in sorted(module.classes.keys()):
             _class = module.classes[className]
             classNameStr = "{0}{1}.".format(moduleName, className)
             for variable in sorted(_class.attributes.keys()):
                 if not _class.attributes[variable].isPrivate():
                     from QScintilla.Editor import Editor
+
                     if _class.attributes[variable].isPublic():
                         iconId = Editor.AttributeID
                     elif _class.attributes[variable].isProtected():
                         iconId = Editor.AttributeProtectedID
                     else:
                         iconId = Editor.AttributePrivateID
-                    api.append('{0}{1}?{2:d}'.format(classNameStr, variable,
-                                                     iconId))
+                    api.append("{0}{1}?{2:d}".format(classNameStr, variable, iconId))
         return api
-    
+
     def __loadApiFile(self, apiFile):
         """
         Private method to read a raw API file into the database.
-        
+
         @param apiFile filename of the raw API file
         @type str
         """
         apis = []
         bases = []
-        
+
         if self.__language == ApisNameProject:
             with contextlib.suppress(OSError, ImportError):
                 module = Utilities.ModuleParser.readModule(
                     os.path.join(self.__projectPath, apiFile),
                     basename=self.__projectPath + os.sep,
-                    caching=False)
+                    caching=False,
+                )
                 language = module.getType()
                 if language:
                     from DocumentationTools.APIGenerator import APIGenerator
+
                     apiGenerator = APIGenerator(module)
                     apis = apiGenerator.genAPI(True, "", True)
                     if os.path.basename(apiFile).startswith("Ui_"):
                         # it is a forms source file, extract public attributes
                         # as well
                         apis.extend(self.__classesAttributesApi(module))
-                    
+
                     basesDict = apiGenerator.genBases(True)
                     for baseEntry in basesDict:
                         if basesDict[baseEntry]:
-                            bases.append("{0} {1}\n".format(
-                                baseEntry, " ".join(
-                                    sorted(basesDict[baseEntry]))))
+                            bases.append(
+                                "{0} {1}\n".format(
+                                    baseEntry, " ".join(sorted(basesDict[baseEntry]))
+                                )
+                            )
         else:
             with contextlib.suppress(OSError, UnicodeError):
                 apis = Utilities.readEncodedFile(apiFile)[0].splitlines(True)
             with contextlib.suppress(OSError, UnicodeError):
                 basesFile = os.path.splitext(apiFile)[0] + ".bas"
                 if os.path.exists(basesFile):
-                    bases = (
-                        Utilities.readEncodedFile(basesFile)[0]
-                        .splitlines(True)
-                    )
+                    bases = Utilities.readEncodedFile(basesFile)[0].splitlines(True)
             language = None
-        
+
         if len(apis) > 0:
             self.__storeApis(apis, bases, apiFile, language)
         else:
             # just store file info to avoid rereading it every time
             self.__storeFileInfoOnly(apiFile)
-    
+
     def __storeFileInfoOnly(self, apiFile):
         """
         Private method to store file info only.
-        
+
         Doing this avoids rereading the file whenever the API is initialized
         in case the given file doesn't contain API data.
-        
+
         @param apiFile file name of the API file
         @type str
         """
@@ -288,7 +297,7 @@
             query.prepare(self.populate_file_stmt)
             query.bindValue(":file", apiFile)
             query.exec()
-            
+
             # step 2: update the file entry
             query.prepare(self.update_file_stmt)
             query.bindValue(":lastRead", QDateTime.currentDateTime())
@@ -301,11 +310,11 @@
                 db.rollback()
             else:
                 db.commit()
-    
+
     def __storeApis(self, apis, bases, apiFile, language):
         """
         Private method to put the API entries into the database.
-        
+
         @param apis list of api entries
         @type list of str
         @param bases list of base class entries
@@ -332,43 +341,43 @@
             query.bindValue(":file", apiFile)
             if not query.exec():
                 return
-            if not query.next():        # __IGNORE_WARNING_M513__
+            if not query.next():  # __IGNORE_WARNING_M513__
                 return
             fileId = int(query.value(0))
-            
+
             # step 2: delete all entries belonging to this file
             query.prepare(self.populate_del_api_stmt)
             query.bindValue(":fileId", fileId)
             query.exec()
-            
+
             query.prepare(self.populate_del_bases_stmt)
             query.bindValue(":fileId", fileId)
             query.exec()
-            
+
             # step 3: load the given API info
             query.prepare(self.populate_api_stmt)
             for api in apis:
                 if self.__aborted:
                     break
-                
+
                 api = api.strip()
                 if len(api) == 0:
                     continue
-                
-                b = api.find('(')
+
+                b = api.find("(")
                 if b == -1:
                     path = api
                     sig = ""
                 else:
                     path = api[:b]
                     sig = api[b:]
-                
+
                 while len(path) > 0:
                     acWord = ""
                     context = ""
                     fullContext = ""
                     pictureId = ""
-                    
+
                     # search for word separators
                     index = len(path)
                     while index > 0:
@@ -382,7 +391,7 @@
                             if acWord == "":
                                 # completion found
                                 acWord = path[index:]
-                                path = path[:(index - len(wsep))]
+                                path = path[: (index - len(wsep))]
                                 index = len(path)
                                 fullContext = path
                                 context = path
@@ -395,7 +404,7 @@
                     if acWord == "":
                         acWord = path
                         path = ""
-                    
+
                     query.bindValue(":acWord", acWord)
                     query.bindValue(":context", context)
                     query.bindValue(":fullContext", fullContext)
@@ -403,25 +412,25 @@
                     query.bindValue(":fileId", fileId)
                     query.bindValue(":pictureId", pictureId)
                     query.exec()
-                    
+
                     sig = ""
-            
+
             # step 4: load the given base classes info
             query.prepare(self.populate_bases_stmt)
             for base in bases:
                 if self.__aborted:
                     break
-                
+
                 base = base.strip()
                 if len(base) == 0:
                     continue
-                
+
                 class_, baseClasses = base.split(" ", 1)
                 query.bindValue(":class", class_)
                 query.bindValue(":baseClasses", baseClasses)
                 query.bindValue(":fileId", fileId)
                 query.exec()
-            
+
             if not self.__aborted:
                 # step 5: update the file entry
                 query.prepare(self.update_file_stmt)
@@ -435,11 +444,11 @@
                 db.rollback()
             else:
                 db.commit()
-    
+
     def __deleteApiFile(self, apiFile):
         """
         Private method to delete all references to an api file.
-        
+
         @param apiFile filename of the raw API file
         @type str
         """
@@ -447,24 +456,24 @@
         db.transaction()
         try:
             query = QSqlQuery(db)
-            
+
             # step 1: get the ID belonging to the api file
             query.prepare(self.file_id_stmt)
             query.bindValue(":file", apiFile)
             query.exec()
-            query.next()                    # __IGNORE_WARNING_M513__
+            query.next()  # __IGNORE_WARNING_M513__
             fileId = int(query.value(0))
-            
+
             # step 2: delete all API entries belonging to this file
             query.prepare(self.populate_del_api_stmt)
             query.bindValue(":fileId", fileId)
             query.exec()
-            
+
             # step 3: delete all base classes entries belonging to this file
             query.prepare(self.populate_del_bases_stmt)
             query.bindValue(":fileId", fileId)
             query.exec()
-            
+
             # step 4: delete the file entry
             query.prepare(self.file_delete_id_stmt)
             query.bindValue(":id", fileId)
@@ -477,39 +486,39 @@
     def __getApiFiles(self):
         """
         Private method to get a list of API files loaded into the database.
-        
+
         @return list of API filenames
         @rtype list of str
         """
         apiFiles = []
-        
+
         db = QSqlDatabase.database(self.__connectionName)
         db.transaction()
         try:
             query = QSqlQuery(db)
             query.exec(self.api_files_stmt)
-            while query.next():                     # __IGNORE_WARNING_M513__
+            while query.next():  # __IGNORE_WARNING_M513__
                 apiFiles.append(query.value(0))
         finally:
             query.finish()
             del query
             db.commit()
-        
+
         return apiFiles
-    
+
     def run(self):
         """
         Public method to perform the threads work.
         """
         self.processing.emit(WorkerStatusStarted, "")
-        
+
         if QSqlDatabase.contains(self.__connectionName):
             QSqlDatabase.removeDatabase(self.__connectionName)
-        
+
         db = QSqlDatabase.addDatabase("QSQLITE", self.__connectionName)
         db.setDatabaseName(self.__databaseName)
         db.open()
-        
+
         if db.isValid() and db.isOpen():
             # step 1: remove API files not wanted any longer
             if not self.__refresh:
@@ -517,14 +526,14 @@
                 for apiFile in loadedApiFiles:
                     if not self.__aborted and apiFile not in self.__apiFiles:
                         self.__deleteApiFile(apiFile)
-            
+
             # step 2: (re-)load api files
             for apiFile in self.__apiFiles:
                 if not self.__aborted:
                     self.__loadApiFileIfNewer(apiFile)
-        
+
         db.close()
-        
+
         if self.__aborted:
             self.processing.emit(WorkerStatusAborted, "")
         else:
@@ -534,20 +543,21 @@
 class DbAPIs(QObject):
     """
     Class implementing an API storage entity.
-    
+
     @signal apiPreparationStatus(language, status, file) emitted to indicate
         the API preparation status for a language
     """
+
     apiPreparationStatus = pyqtSignal(str, int, str)
-    
+
     DB_VERSION = 4
-    
+
     create_mgmt_stmt = """
         CREATE TABLE mgmt
         (format INTEGER)
     """
     drop_mgmt_stmt = """DROP TABLE IF EXISTS mgmt"""
-    
+
     create_api_stmt = """
         CREATE TABLE api
         (acWord TEXT,
@@ -560,7 +570,7 @@
         )
     """
     drop_api_stmt = """DROP TABLE IF EXISTS api"""
-    
+
     create_bases_stmt = """
         CREATE TABLE bases
         (class TEXT UNIQUE ON CONFLICT IGNORE,
@@ -569,7 +579,7 @@
         )
     """
     drop_bases_stmt = """DROP TABLE IF EXISTS bases"""
-    
+
     create_file_stmt = """
         CREATE TABLE file
         (id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -578,21 +588,19 @@
         )
     """
     drop_file_stmt = """DROP TABLE IF EXISTS file"""
-    
+
     create_acWord_idx = """CREATE INDEX acWord_idx on api (acWord)"""
     drop_acWord_idx = """DROP INDEX IF EXISTS acWord_idx"""
-    
+
     create_context_idx = """CREATE INDEX context_idx on api (context)"""
     drop_context_idx = """DROP INDEX IF EXISTS context_idx"""
-    
-    create_fullContext_idx = (
-        """CREATE INDEX fullContext_idx on api (fullContext)"""
-    )
+
+    create_fullContext_idx = """CREATE INDEX fullContext_idx on api (fullContext)"""
     drop_fullContext_idx = """DROP INDEX IF EXISTS fullContext_idx"""
-    
+
     create_bases_idx = """CREATE INDEX base_idx on bases (class)"""
     drop_bases_idx = """DROP INDEX IF EXISTS base_idx"""
-    
+
     create_file_idx = """CREATE INDEX file_idx on file (file)"""
     drop_file_idx = """DROP INDEX IF EXISTS file_idx"""
 
@@ -638,12 +646,14 @@
     """
     mgmt_insert_stmt = """
         INSERT INTO mgmt (format) VALUES ({0:d})
-        """.format(DB_VERSION)
-    
+        """.format(
+        DB_VERSION
+    )
+
     def __init__(self, language, projectType="", parent=None):
         """
         Constructor
-        
+
         @param language language of the APIs object
         @type str
         @param projectType type of the project
@@ -656,42 +666,43 @@
             self.setObjectName("DbAPIs_{0}_{1}".format(language, projectType))
         else:
             self.setObjectName("DbAPIs_{0}".format(language))
-        
+
         self.__inPreparation = False
         self.__worker = None
         self.__workerQueue = []
         self.__opened = False
-        
+
         self.__projectType = projectType
         self.__language = language
-        
+
         if self.__projectType:
             self.__connectionName = "{0}_{1}".format(
-                self.__language, self.__projectType)
+                self.__language, self.__projectType
+            )
         else:
             self.__connectionName = self.__language
-        
+
         if self.__language == ApisNameProject:
             self.__initAsProject()
         else:
             self.__initAsLanguage()
-    
+
     def __initAsProject(self):
         """
         Private method to initialize as a project API object.
         """
         self.__lexer = None
-        
+
         self.__project = ericApp().getObject("Project")
         self.__project.projectOpened.connect(self.__projectOpened)
         self.__project.newProject.connect(self.__projectOpened)
         self.__project.projectClosed.connect(self.__projectClosed)
         self.__project.projectFormCompiled.connect(self.__projectFormCompiled)
         self.__project.projectChanged.connect(self.__projectChanged)
-        
+
         if self.__project.isOpen():
             self.__projectOpened()
-    
+
     def __initAsLanguage(self):
         """
         Private method to initialize as a language API object.
@@ -703,35 +714,36 @@
         self.__lexer = QScintilla.Lexers.getLexer(self.__language)
         try:
             self.__apifiles = Preferences.getEditorAPI(
-                self.__language, projectType=self.__projectType)
+                self.__language, projectType=self.__projectType
+            )
         except TypeError:
             # older interface
             self.__apifiles = Preferences.getEditorAPI(self.__language)
         self.__apifiles.sort()
         if self.__lexer is not None:
             self.__openAPIs()
-    
+
     def _apiDbName(self):
         """
         Protected method to determine the name of the database file.
-        
+
         @return name of the database file
         @rtype str
         """
         if self.__language == ApisNameProject:
-            return os.path.join(self.__project.getProjectManagementDir(),
-                                "project-apis.db")
+            return os.path.join(
+                self.__project.getProjectManagementDir(), "project-apis.db"
+            )
         else:
             apisDir = os.path.join(Globals.getConfigDir(), "APIs")
             if not os.path.exists(apisDir):
                 os.makedirs(apisDir)
             if self.__projectType:
-                filename = "{0}_{1}-api.db".format(self.__language,
-                                                   self.__projectType)
+                filename = "{0}_{1}-api.db".format(self.__language, self.__projectType)
             else:
                 filename = "{0}-api.db".format(self.__language)
             return os.path.join(apisDir, filename)
-    
+
     def close(self):
         """
         Public method to close the database.
@@ -741,25 +753,21 @@
             self.__worker.abort()
             if self.__worker is not None:
                 self.__worker.wait(5000)
-                if (
-                    self.__worker is not None and
-                    not self.__worker.isFinished()
-                ):
+                if self.__worker is not None and not self.__worker.isFinished():
                     self.__worker.terminate()
                     if self.__worker is not None:
                         self.__worker.wait(5000)
-        
-        if QSqlDatabase and QSqlDatabase.database(
-                self.__connectionName).isOpen():
+
+        if QSqlDatabase and QSqlDatabase.database(self.__connectionName).isOpen():
             QSqlDatabase.database(self.__connectionName).close()
             QSqlDatabase.removeDatabase(self.__connectionName)
-        
+
         self.__opened = False
-        
+
     def __openApiDb(self):
         """
         Private method to open the API database.
-        
+
         @return flag indicating the database status
         @rtype bool
         """
@@ -768,9 +776,8 @@
             # the database connection is a new one
             db = QSqlDatabase.addDatabase("QSQLITE", self.__connectionName)
             dbName = self._apiDbName()
-            if (
-                self.__language == ApisNameProject and
-                not os.path.exists(self.__project.getProjectManagementDir())
+            if self.__language == ApisNameProject and not os.path.exists(
+                self.__project.getProjectManagementDir()
             ):
                 opened = False
             else:
@@ -781,7 +788,7 @@
         else:
             opened = True
         return opened
-    
+
     def __createApiDB(self):
         """
         Private method to create an API database.
@@ -821,30 +828,30 @@
     def getApiFiles(self):
         """
         Public method to get a list of API files loaded into the database.
-        
+
         @return list of API filenames
         @rtype list of str
         """
         apiFiles = []
-        
+
         db = QSqlDatabase.database(self.__connectionName)
         db.transaction()
         try:
             query = QSqlQuery(db)
             query.exec(self.api_files_stmt)
-            while query.next():                     # __IGNORE_WARNING_M513__
+            while query.next():  # __IGNORE_WARNING_M513__
                 apiFiles.append(query.value(0))
         finally:
             query.finish()
             del query
             db.commit()
-        
+
         return apiFiles
-    
+
     def __isPrepared(self):
         """
         Private method to check, if the database has been prepared.
-        
+
         @return flag indicating the prepared status
         @rtype bool
         """
@@ -857,7 +864,7 @@
                 query = QSqlQuery(db)
                 ok = query.exec(self.format_select_stmt)
                 if ok:
-                    query.next()                    # __IGNORE_WARNING_M513__
+                    query.next()  # __IGNORE_WARNING_M513__
                     formatVersion = int(query.value(0))
                     if formatVersion >= self.DB_VERSION:
                         prepared = True
@@ -866,11 +873,11 @@
                 del query
                 db.commit()
         return prepared
-    
+
     def getCompletions(self, start=None, context=None, followHierarchy=False):
         """
         Public method to determine the possible completions.
-        
+
         @param start string giving the start of the word to be
             completed
         @type str
@@ -887,60 +894,72 @@
         @rtype list of dict
         """
         completions = []
-        
+
         db = QSqlDatabase.database(self.__connectionName)
         if db.isOpen() and not self.__inPreparation:
             db.transaction()
             try:
                 query = None
-                
+
                 if start is not None and context is not None:
                     query = QSqlQuery(db)
                     query.prepare(self.ac_context_word_stmt)
-                    query.bindValue(":acWord", start + '*')
+                    query.bindValue(":acWord", start + "*")
                     query.bindValue(":context", context)
                 elif start is not None:
                     query = QSqlQuery(db)
                     query.prepare(self.ac_stmt)
-                    query.bindValue(":acWord", start + '*')
+                    query.bindValue(":acWord", start + "*")
                 elif context is not None:
                     query = QSqlQuery(db)
                     query.prepare(self.ac_context_stmt)
                     query.bindValue(":context", context)
-                
+
                 if query is not None:
                     query.exec()
-                    while query.next():             # __IGNORE_WARNING_M513__
-                        completions.append({"completion": query.value(0),
-                                            "context": query.value(1),
-                                            "pictureId": query.value(2)})
+                    while query.next():  # __IGNORE_WARNING_M513__
+                        completions.append(
+                            {
+                                "completion": query.value(0),
+                                "context": query.value(1),
+                                "pictureId": query.value(2),
+                            }
+                        )
                     query.finish()
                     del query
             finally:
                 db.commit()
-            
+
             if followHierarchy:
                 query = QSqlQuery(db)
                 query.prepare(self.bases_stmt)
                 query.bindValue(":class", context)
                 query.exec()
-                if query.next():                # __IGNORE_WARNING_M513__
+                if query.next():  # __IGNORE_WARNING_M513__
                     bases = query.value(0).split()
                 else:
                     bases = []
                 for base in bases:
-                    completions.extend(self.getCompletions(start, base,
-                                       followHierarchy=True))
+                    completions.extend(
+                        self.getCompletions(start, base, followHierarchy=True)
+                    )
                 query.finish()
                 del query
-        
+
         return completions
-    
-    def getCalltips(self, acWord, commas, context=None, fullContext=None,
-                    showContext=True, followHierarchy=False):
+
+    def getCalltips(
+        self,
+        acWord,
+        commas,
+        context=None,
+        fullContext=None,
+        showContext=True,
+        followHierarchy=False,
+    ):
         """
         Public method to determine the calltips.
-        
+
         @param acWord function to get calltips for
         @type str
         @param commas minimum number of commas contained in the calltip
@@ -958,7 +977,7 @@
         @rtype list of str
         """
         calltips = []
-        
+
         db = QSqlDatabase.database(self.__connectionName)
         if db.isOpen() and not self.__inPreparation:
             if self.autoCompletionWordSeparators():
@@ -978,7 +997,7 @@
                     query.prepare(self.ct_stmt)
                 query.bindValue(":acWord", acWord)
                 query.exec()
-                while query.next():             # __IGNORE_WARNING_M513__
+                while query.next():  # __IGNORE_WARNING_M513__
                     word = query.value(0)
                     sig = query.value(1)
                     fullCtx = query.value(2)
@@ -990,45 +1009,54 @@
                             sig = sig.strip(", \t\r\n")
                         if self.__enoughCommas(sig, commas):
                             if showContext:
-                                calltips.append("{0}{1}{2}{3}".format(
-                                    fullCtx,
-                                    contextSeparator if fullCtx else "",
-                                    word, sig))
+                                calltips.append(
+                                    "{0}{1}{2}{3}".format(
+                                        fullCtx,
+                                        contextSeparator if fullCtx else "",
+                                        word,
+                                        sig,
+                                    )
+                                )
                             else:
                                 calltips.append("{0}{1}".format(word, sig))
                 query.finish()
                 del query
             finally:
                 db.commit()
-            
+
             if followHierarchy:
                 query = QSqlQuery(db)
                 query.prepare(self.bases_stmt)
                 query.bindValue(":class", context)
                 query.exec()
-                if query.next():                # __IGNORE_WARNING_M513__
+                if query.next():  # __IGNORE_WARNING_M513__
                     bases = query.value(0).split()
                 else:
                     bases = []
                 for base in bases:
-                    calltips.extend(self.getCalltips(
-                        acWord, commas, context=base, showContext=showContext,
-                        followHierarchy=True))
+                    calltips.extend(
+                        self.getCalltips(
+                            acWord,
+                            commas,
+                            context=base,
+                            showContext=showContext,
+                            followHierarchy=True,
+                        )
+                    )
                 query.finish()
                 del query
-            
+
             if context and len(calltips) == 0 and not followHierarchy:
                 # nothing found, try without a context
-                calltips = self.getCalltips(
-                    acWord, commas, showContext=showContext)
-        
+                calltips = self.getCalltips(acWord, commas, showContext=showContext)
+
         return calltips
-    
+
     def __enoughCommas(self, s, commas):
         """
         Private method to determine, if the given string contains enough
         commas.
-        
+
         @param s string to check
         @type str
         @param commas number of commas to check for
@@ -1036,14 +1064,14 @@
         @return flag indicating, that there are enough commas
         @rtype bool
         """
-        end = s.find(')')
-        
+        end = s.find(")")
+
         if end < 0:
             return False
-        
+
         w = s[:end]
-        return w.count(',') >= commas
-    
+        return w.count(",") >= commas
+
     def __openAPIs(self):
         """
         Private method to open the API database.
@@ -1052,28 +1080,26 @@
         if self.__opened:
             if not self.__isPrepared():
                 self.__createApiDB()
-            
+
             # prepare the database if neccessary
             self.prepareAPIs()
-    
+
     def __getProjectFormSources(self, normalized=False):
         """
         Private method to get the source files for the project forms.
-        
+
         @param normalized flag indicating a normalized filename is wanted
         @type bool
         @return list of project form sources
         @rtype list of str
         """
-        if self.__project.getProjectLanguage() in (
-            "Python3", "MicroPython", "Cython"
-        ):
+        if self.__project.getProjectLanguage() in ("Python3", "MicroPython", "Cython"):
             sourceExt = ".py"
         elif self.__project.getProjectLanguage() == "Ruby":
             sourceExt = ".rb"
         else:
             return []
-        
+
         formsSources = []
         forms = self.__project.getProjectFiles("FORMS")
         for fn in forms:
@@ -1081,42 +1107,48 @@
             dirname, filename = os.path.split(ofn)
             formSource = os.path.join(dirname, "Ui_" + filename + sourceExt)
             if normalized:
-                formSource = os.path.join(
-                    self.__project.getProjectPath(), formSource)
+                formSource = os.path.join(self.__project.getProjectPath(), formSource)
             formsSources.append(formSource)
         return formsSources
-    
+
     def prepareAPIs(self, rawList=None):
         """
         Public method to prepare the APIs if neccessary.
-        
+
         @param rawList list of raw API files
         @type list of str
         """
         if self.__inPreparation:
             return
-        
+
         projectPath = ""
         if rawList:
             apiFiles = rawList[:]
         elif self.__language == ApisNameProject:
             apiFiles = self.__project.getSources()[:]
             apiFiles.extend(
-                [f for f in self.__getProjectFormSources() if
-                 f not in apiFiles])
+                [f for f in self.__getProjectFormSources() if f not in apiFiles]
+            )
             projectPath = self.__project.getProjectPath()
             projectType = ""
         else:
             apiFiles = Preferences.getEditorAPI(
-                self.__language, projectType=self.__projectType)
+                self.__language, projectType=self.__projectType
+            )
             projectType = self.__projectType
-        self.__worker = DbAPIsWorker(self, self.__language, apiFiles,
-                                     self._apiDbName(), projectPath,
-                                     projectType=projectType)
+        self.__worker = DbAPIsWorker(
+            self,
+            self.__language,
+            apiFiles,
+            self._apiDbName(),
+            projectPath,
+            projectType=projectType,
+        )
         self.__worker.processing.connect(
-            self.__processingStatus, Qt.ConnectionType.QueuedConnection)
+            self.__processingStatus, Qt.ConnectionType.QueuedConnection
+        )
         self.__worker.start()
-    
+
     def __processQueue(self):
         """
         Private slot to process the queue of files to load.
@@ -1124,7 +1156,7 @@
         if self.__worker is not None and self.__worker.isFinished():
             self.__worker.deleteLater()
             self.__worker = None
-        
+
         if self.__worker is None and len(self.__workerQueue) > 0:
             apiFiles = [self.__workerQueue.pop(0)]
             if self.__language == ApisNameProject:
@@ -1134,38 +1166,45 @@
             else:
                 projectPath = ""
                 projectType = self.__projectType
-            self.__worker = DbAPIsWorker(self, self.__language, apiFiles,
-                                         self._apiDbName(), projectPath,
-                                         projectType=projectType, refresh=True)
+            self.__worker = DbAPIsWorker(
+                self,
+                self.__language,
+                apiFiles,
+                self._apiDbName(),
+                projectPath,
+                projectType=projectType,
+                refresh=True,
+            )
             self.__worker.processing.connect(
-                self.__processingStatus, Qt.ConnectionType.QueuedConnection)
+                self.__processingStatus, Qt.ConnectionType.QueuedConnection
+            )
             self.__worker.start()
-    
+
     def getLexer(self):
         """
         Public method to return a reference to our lexer object.
-        
+
         @return reference to the lexer object
         @rtype Lexer
         """
         return self.__lexer
-    
+
     def autoCompletionWordSeparators(self):
         """
         Public method to get the word separator characters.
-        
+
         @return word separator characters
         @rtype list of str
         """
         if self.__lexer:
             return self.__lexer.autoCompletionWordSeparators()
         return None
-    
+
     def __processingStatus(self, status, filename):
         """
         Private slot handling the processing signal of the API preparation
         thread.
-        
+
         @param status preparation status (one of WorkerStatus...)
         @type int
         @param filename name of the file being processed
@@ -1173,56 +1212,49 @@
         """
         if status == WorkerStatusStarted:
             self.__inPreparation = True
-            self.apiPreparationStatus.emit(
-                self.__language, WorkerStatusStarted, "")
+            self.apiPreparationStatus.emit(self.__language, WorkerStatusStarted, "")
         elif status == WorkerStatusFinished:
             self.__inPreparation = False
-            self.apiPreparationStatus.emit(
-                self.__language, WorkerStatusFinished, "")
+            self.apiPreparationStatus.emit(self.__language, WorkerStatusFinished, "")
             QTimer.singleShot(0, self.__processQueue)
         elif status == WorkerStatusAborted:
             self.__inPreparation = False
-            self.apiPreparationStatus.emit(
-                self.__language, WorkerStatusAborted, "")
+            self.apiPreparationStatus.emit(self.__language, WorkerStatusAborted, "")
             QTimer.singleShot(0, self.__processQueue)
         elif status == WorkerStatusFile:
-            self.apiPreparationStatus.emit(
-                self.__language, WorkerStatusFile, filename)
-    
+            self.apiPreparationStatus.emit(self.__language, WorkerStatusFile, filename)
+
     ########################################################
     ## project related stuff below
     ########################################################
-    
+
     def __projectOpened(self):
         """
         Private slot to perform actions after a project has been opened.
         """
-        if self.__project.getProjectLanguage() in (
-            "Python3", "MicroPython", "Cython"
-        ):
+        if self.__project.getProjectLanguage() in ("Python3", "MicroPython", "Cython"):
             self.__discardFirst = ["self", "cls"]
         else:
             self.__discardFirst = []
-        self.__lexer = QScintilla.Lexers.getLexer(
-            self.__project.getProjectLanguage())
+        self.__lexer = QScintilla.Lexers.getLexer(self.__project.getProjectLanguage())
         self.__openAPIs()
-    
+
     def __projectClosed(self):
         """
         Private slot to perform actions after a project has been closed.
         """
         self.close()
-    
+
     def __projectFormCompiled(self, filename):
         """
         Private slot to handle the projectFormCompiled signal.
-        
+
         @param filename name of the form file that was compiled
         @type str
         """
         self.__workerQueue.append(filename)
         self.__processQueue()
-    
+
     def __projectChanged(self):
         """
         Private slot to handle the projectChanged signal.
@@ -1230,11 +1262,11 @@
         if self.__opened:
             self.__projectClosed()
             self.__projectOpened()
-    
+
     def editorSaved(self, filename):
         """
         Public slot to handle the editorSaved signal.
-        
+
         @param filename name of the file that was saved
         @type str
         """
@@ -1248,10 +1280,11 @@
     Class implementing the APIsManager class, which is the central store for
     API information used by autocompletion and calltips.
     """
+
     def __init__(self, mainWindow, parent=None):
         """
         Constructor
-        
+
         @param mainWindow reference to the main eric7 window
         @type QMainWindow
         @param parent reference to the parent object
@@ -1259,27 +1292,27 @@
         """
         QObject.__init__(self, parent)
         self.setObjectName("Assistant_APIsManager")
-        
+
         self.__mw = mainWindow
-        
+
         # initialize the apis dictionary
         self.__apis = {}
-    
+
     def reloadAPIs(self):
         """
         Public slot to reload the api information.
         """
         for api in list(self.__apis.values()):
             api and api.prepareAPIs()
-    
+
     def getAPIs(self, language, projectType=""):
         """
         Public method to get an apis object for autocompletion/calltips.
-        
+
         This method creates and loads an APIs object dynamically upon request.
         This saves memory for languages, that might not be needed at the
         moment.
-        
+
         @param language language of the requested APIs object
         @type str
         @param projectType type of the project
@@ -1291,8 +1324,8 @@
             return self.__apis[(language, projectType)]
         except KeyError:
             if (
-                language in QScintilla.Lexers.getSupportedApiLanguages() or
-                language == ApisNameProject
+                language in QScintilla.Lexers.getSupportedApiLanguages()
+                or language == ApisNameProject
             ):
                 # create the api object
                 api = DbAPIs(language, projectType=projectType)
@@ -1301,7 +1334,7 @@
                 return self.__apis[(language, projectType)]
             else:
                 return None
-    
+
     def deactivate(self):
         """
         Public method to perform actions upon deactivation.
@@ -1310,21 +1343,21 @@
             self.__apis[apiLang].close()
             self.__apis[apiLang].deleteLater()
             self.__apis[apiLang] = None
-    
+
     def __showMessage(self, msg):
         """
         Private message to show a message in the main windows status bar.
-        
+
         @param msg message to be shown
         @type str
         """
         if msg:
             self.__mw.statusBar().showMessage(msg, 2000)
-    
+
     def __apiPreparationStatus(self, language, status, filename):
         """
         Private slot handling the preparation status signal of an API object.
-        
+
         @param language language of the API
         @type str
         @param status preparation status (one of WorkerStatus...)
@@ -1334,20 +1367,26 @@
         """
         if language == ApisNameProject:
             language = self.tr("Project")
-        
+
         if status == WorkerStatusStarted:
-            self.__showMessage(self.tr(
-                "Preparation of '{0}' APIs started.").format(language))
+            self.__showMessage(
+                self.tr("Preparation of '{0}' APIs started.").format(language)
+            )
         elif status == WorkerStatusFile:
-            self.__showMessage(self.tr(
-                "'{0}' APIs: Processing '{1}'").format(
-                language, os.path.basename(filename)))
+            self.__showMessage(
+                self.tr("'{0}' APIs: Processing '{1}'").format(
+                    language, os.path.basename(filename)
+                )
+            )
         elif status == WorkerStatusFinished:
-            self.__showMessage(self.tr(
-                "Preparation of '{0}' APIs finished.").format(language))
+            self.__showMessage(
+                self.tr("Preparation of '{0}' APIs finished.").format(language)
+            )
         elif status == WorkerStatusAborted:
-            self.__showMessage(self.tr(
-                "Preparation of '{0}' APIs cancelled.").format(language))
+            self.__showMessage(
+                self.tr("Preparation of '{0}' APIs cancelled.").format(language)
+            )
+
 
 #
 # eflag: noqa = M523, M834, S608

eric ide

mercurial