--- 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