AssistantEric/APIsManager.py

changeset 71
025683852a63
parent 69
9082f14126d9
child 74
9ac338de7a2b
child 76
add31149e0b1
equal deleted inserted replaced
70:20a97b9662c6 71:025683852a63
7 Module implementing the APIsManager. 7 Module implementing the APIsManager.
8 """ 8 """
9 9
10 import os 10 import os
11 11
12 from PyQt4.QtCore import QTimer, QThread, QFileInfo, pyqtSignal, QCoreApplication, \ 12 from PyQt4.QtCore import QTimer, QThread, QFileInfo, pyqtSignal, QDateTime, QObject, Qt
13 QEvent, QDateTime, QObject, Qt
14 try: 13 try:
15 from PyQt4.QtSql import QSqlDatabase, QSqlQuery 14 from PyQt4.QtSql import QSqlDatabase, QSqlQuery
16 except ImportError: 15 except ImportError:
17 # just ignore it because the main plug-in file will deal with it 16 # just ignore it because the main plug-in file will deal with it
18 pass 17 pass
23 22
24 import Utilities.ModuleParser 23 import Utilities.ModuleParser
25 import Utilities 24 import Utilities
26 import Preferences 25 import Preferences
27 26
28 WorkerStarted = QEvent.User + 2001 27 WorkerStatusStarted = 2001
29 WorkerFinished = QEvent.User + 2002 28 WorkerStatusFinished = 2002
30 WorkerAborted = QEvent.User + 2003 29 WorkerStatusAborted = 2003
30 WorkerStatusFile = 2004
31 31
32 ApisNameProject = "__Project__" 32 ApisNameProject = "__Project__"
33 33
34 34
35 class DbAPIsWorker(QThread): 35 class DbAPIsWorker(QThread):
36 """ 36 """
37 Class implementing a worker thread to prepare the API database. 37 Class implementing a worker thread to prepare the API database.
38 """ 38
39 @signal processing(status, file) emitted to indicate the processing status
40 (one of WorkerStatus..., string)
41 """
42 processing = pyqtSignal(int, str)
43
39 populate_api_stmt = """ 44 populate_api_stmt = """
40 INSERT INTO api (acWord, context, fullContext, signature, fileId, pictureId) 45 INSERT INTO api (acWord, context, fullContext, signature, fileId, pictureId)
41 VALUES (:acWord, :context, :fullContext, :signature, :fileId, :pictureId) 46 VALUES (:acWord, :context, :fullContext, :signature, :fileId, :pictureId)
42 """ 47 """
43 populate_del_api_stmt = """ 48 populate_del_api_stmt = """
142 if os.path.exists(basesFile): 147 if os.path.exists(basesFile):
143 modTimeBases = QFileInfo(basesFile).lastModified() 148 modTimeBases = QFileInfo(basesFile).lastModified()
144 if modTimeBases > modTime: 149 if modTimeBases > modTime:
145 modTime = modTimeBases 150 modTime = modTimeBases
146 if loadTime < modTime: 151 if loadTime < modTime:
152 self.processing.emit(WorkerStatusFile, apiFile)
147 self.__loadApiFile(apiFile) 153 self.__loadApiFile(apiFile)
148 154
149 def __classesAttributesApi(self, module): 155 def __classesAttributesApi(self, module):
150 """ 156 """
151 Private method to generate class api section for class attributes. 157 Private method to generate class api section for class attributes.
219 pass 225 pass
220 language = None 226 language = None
221 227
222 if len(apis) > 0: 228 if len(apis) > 0:
223 self.__storeApis(apis, bases, apiFile, language) 229 self.__storeApis(apis, bases, apiFile, language)
230 else:
231 # just store file info to avoid rereading it every time
232 self.__storeFileInfoOnly(apiFile)
233
234 def __storeFileInfoOnly(self, apiFile):
235 """
236 Private method to store file info only.
237
238 Doing this avoids rereading the file whenever the API is initialized in case
239 the given file doesn't contain API data.
240 """
241 db = QSqlDatabase.database(self.__language)
242 db.transaction()
243 try:
244 query = QSqlQuery(db)
245 # step 1: create entry in file table
246 query.prepare(self.populate_file_stmt)
247 query.bindValue(":file", apiFile)
248 query.exec_()
249
250 # step 2: update the file entry
251 query.prepare(self.update_file_stmt)
252 query.bindValue(":lastRead", QDateTime.currentDateTime())
253 query.bindValue(":file", apiFile)
254 query.exec_()
255 finally:
256 del query
257 if self.__aborted:
258 db.rollback()
259 else:
260 db.commit()
224 261
225 def __storeApis(self, apis, bases, apiFile, language): 262 def __storeApis(self, apis, bases, apiFile, language):
226 """ 263 """
227 Private method to put the API entries into the database. 264 Private method to put the API entries into the database.
228 265
339 query.bindValue(":baseClasses", baseClasses) 376 query.bindValue(":baseClasses", baseClasses)
340 query.bindValue(":fileId", id) 377 query.bindValue(":fileId", id)
341 query.exec_() 378 query.exec_()
342 379
343 if not self.__aborted: 380 if not self.__aborted:
344 # step 4: update the file entry 381 # step 5: update the file entry
345 query.prepare(self.update_file_stmt) 382 query.prepare(self.update_file_stmt)
346 query.bindValue(":lastRead", QDateTime.currentDateTime()) 383 query.bindValue(":lastRead", QDateTime.currentDateTime())
347 query.bindValue(":file", apiFile) 384 query.bindValue(":file", apiFile)
348 query.exec_() 385 query.exec_()
349 finally: 386 finally:
391 428
392 def run(self): 429 def run(self):
393 """ 430 """
394 Public method to perform the threads work. 431 Public method to perform the threads work.
395 """ 432 """
396 QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerStarted))) 433 self.processing.emit(WorkerStatusStarted, "")
397 434
398 db = QSqlDatabase.database(self.__language) 435 db = QSqlDatabase.database(self.__language)
399 if db.isValid() and db.isOpen(): 436 if db.isValid() and db.isOpen():
400 # step 1: remove API files not wanted any longer 437 # step 1: remove API files not wanted any longer
401 if not self.__refresh: 438 if not self.__refresh:
408 for apiFile in self.__apiFiles: 445 for apiFile in self.__apiFiles:
409 if not self.__aborted: 446 if not self.__aborted:
410 self.__loadApiFileIfNewer(apiFile) 447 self.__loadApiFileIfNewer(apiFile)
411 448
412 if self.__aborted: 449 if self.__aborted:
413 QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerAborted))) 450 self.processing.emit(WorkerStatusAborted, "")
414 else: 451 else:
415 QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerFinished))) 452 self.processing.emit(WorkerStatusFinished, "")
416 453
417 454
418 class DbAPIs(QObject): 455 class DbAPIs(QObject):
419 """ 456 """
420 Class implementing an API storage entity. 457 Class implementing an API storage entity.
421 458
422 @signal apiPreparationFinished(language) emitted after the API preparation has finished 459 @signal apiPreparationStatus(language, status, file) emitted to indicate the
423 @signal apiPreparationStarted(language) emitted after the API preparation has started 460 API preparation status for a language
424 @signal apiPreparationCancelled(language) emitted after the API preparation has been 461 """
425 cancelled 462 apiPreparationStatus = pyqtSignal(str, int, str)
426 """
427 apiPreparationFinished = pyqtSignal(str)
428 apiPreparationStarted = pyqtSignal(str)
429 apiPreparationCancelled = pyqtSignal(str)
430 463
431 DB_VERSION = 4 464 DB_VERSION = 4
432 465
433 create_mgmt_stmt = """ 466 create_mgmt_stmt = """
434 CREATE TABLE mgmt 467 CREATE TABLE mgmt
908 [f for f in self.__getProjectFormSources() if f not in apiFiles]) 941 [f for f in self.__getProjectFormSources() if f not in apiFiles])
909 projectPath = self.__project.getProjectPath() 942 projectPath = self.__project.getProjectPath()
910 else: 943 else:
911 apiFiles = Preferences.getEditorAPI(self.__language) 944 apiFiles = Preferences.getEditorAPI(self.__language)
912 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, projectPath) 945 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, projectPath)
946 self.__worker.processing.connect(self.__processingStatus, Qt.QueuedConnection)
913 self.__worker.start() 947 self.__worker.start()
914 948
915 def __processQueue(self): 949 def __processQueue(self):
916 """ 950 """
917 Private slot to process the queue of files to load. 951 Private slot to process the queue of files to load.
918 """ 952 """
919 if self.__worker is not None and self.__worker.isFinished(): 953 if self.__worker is not None and self.__worker.isFinished():
954 self.__worker.deleteLater()
920 self.__worker = None 955 self.__worker = None
921 956
922 if self.__worker is None and len(self.__workerQueue) > 0: 957 if self.__worker is None and len(self.__workerQueue) > 0:
923 apiFiles = [self.__workerQueue.pop(0)] 958 apiFiles = [self.__workerQueue.pop(0)]
924 if self.__language == ApisNameProject: 959 if self.__language == ApisNameProject:
926 apiFiles = [apiFiles[0].replace(projectPath + os.sep, "")] 961 apiFiles = [apiFiles[0].replace(projectPath + os.sep, "")]
927 else: 962 else:
928 projectPath = "" 963 projectPath = ""
929 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, projectPath, 964 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, projectPath,
930 refresh=True) 965 refresh=True)
966 self.__worker.processing.connect(self.__processingStatus, Qt.QueuedConnection)
931 self.__worker.start() 967 self.__worker.start()
932 968
933 def getLexer(self): 969 def getLexer(self):
934 """ 970 """
935 Public method to return a reference to our lexer object. 971 Public method to return a reference to our lexer object.
946 """ 982 """
947 if self.__lexer: 983 if self.__lexer:
948 return self.__lexer.autoCompletionWordSeparators() 984 return self.__lexer.autoCompletionWordSeparators()
949 return None 985 return None
950 986
951 def event(self, evt): 987 def __processingStatus(self, status, filename):
952 """ 988 """
953 Protected method to handle events from the worker thread. 989 Private slot handling the processing signal of the API preparation
954 990 thread.
955 @param evt reference to the event object (QEvent) 991
956 @return flag indicating, if the event was handled (boolean) 992 @param status preparation status (integer, one of WorkerStatus...)
957 """ 993 @param filename name of the file being processed (string)
958 if evt.type() == WorkerStarted: 994 """
995 if status == WorkerStatusStarted:
959 self.__inPreparation = True 996 self.__inPreparation = True
960 self.apiPreparationStarted.emit(self.__language) 997 self.apiPreparationStatus.emit(self.__language, WorkerStatusStarted, "")
961 return True 998 elif status == WorkerStatusFinished:
962
963 elif evt.type() == WorkerFinished:
964 self.__inPreparation = False 999 self.__inPreparation = False
965 self.apiPreparationFinished.emit(self.__language) 1000 self.apiPreparationStatus.emit(self.__language, WorkerStatusFinished, "")
966 QTimer.singleShot(0, self.__processQueue) 1001 QTimer.singleShot(0, self.__processQueue)
967 return True 1002 elif status == WorkerStatusAborted:
968
969 elif evt.type() == WorkerAborted:
970 self.__inPreparation = False 1003 self.__inPreparation = False
971 self.apiPreparationCancelled.emit(self.__language) 1004 self.apiPreparationStatus.emit(self.__language, WorkerStatusAborted, "")
972 QTimer.singleShot(0, self.__processQueue) 1005 QTimer.singleShot(0, self.__processQueue)
973 return True 1006 elif status == WorkerStatusFile:
974 1007 self.apiPreparationStatus.emit(self.__language, WorkerStatusFile, filename)
975 else:
976 return QObject.event(self, evt)
977 1008
978 ######################################################## 1009 ########################################################
979 ## project related stuff below 1010 ## project related stuff below
980 ######################################################## 1011 ########################################################
981 1012
1058 except KeyError: 1089 except KeyError:
1059 if language in QScintilla.Lexers.getSupportedLanguages() or \ 1090 if language in QScintilla.Lexers.getSupportedLanguages() or \
1060 language == ApisNameProject: 1091 language == ApisNameProject:
1061 # create the api object 1092 # create the api object
1062 api = DbAPIs(language) 1093 api = DbAPIs(language)
1063 api.apiPreparationFinished.connect(self.__apiPreparationFinished) 1094 api.apiPreparationStatus.connect(self.__apiPreparationStatus)
1064 api.apiPreparationStarted.connect(self.__apiPreparationStarted)
1065 api.apiPreparationCancelled.connect(self.__apiPreparationCancelled)
1066 self.__apis[language] = api 1095 self.__apis[language] = api
1067 return self.__apis[language] 1096 return self.__apis[language]
1068 else: 1097 else:
1069 return None 1098 return None
1070 1099
1081 """ 1110 """
1082 Private message to show a message in the main windows status bar. 1111 Private message to show a message in the main windows status bar.
1083 1112
1084 @param msg message to be shown (string) 1113 @param msg message to be shown (string)
1085 """ 1114 """
1086 self.__mw.statusBar().showMessage(msg, 2000) 1115 if msg:
1116 self.__mw.statusBar().showMessage(msg, 2000)
1087 1117
1088 def __apiPreparationFinished(self, language): 1118 def __apiPreparationFinished(self, language):
1089 """ 1119 """
1090 Private slot handling the preparation finished signal of an API object. 1120 Private slot handling the preparation finished signal of an API object.
1091 1121
1115 """ 1145 """
1116 if language == ApisNameProject: 1146 if language == ApisNameProject:
1117 language = self.trUtf8("Project") 1147 language = self.trUtf8("Project")
1118 self.__showMessage(self.trUtf8("Preparation of '{0}' APIs cancelled.") 1148 self.__showMessage(self.trUtf8("Preparation of '{0}' APIs cancelled.")
1119 .format(language)) 1149 .format(language))
1150
1151 def __apiPreparationStatus(self, language, status, filename):
1152 """
1153 Private slot handling the preparation status signal of an API object.
1154
1155 @param language language of the API (string)
1156 @param status preparation status (integer, one of WorkerStatus...)
1157 @param filename name of the file being processed (string)
1158 """
1159 if language == ApisNameProject:
1160 language = self.trUtf8("Project")
1161
1162 if status == WorkerStatusStarted:
1163 self.__showMessage(self.trUtf8("Preparation of '{0}' APIs started.").format(
1164 language))
1165 elif status == WorkerStatusFile:
1166 self.__showMessage(self.trUtf8("'{0}' APIs: Processing '{1}'").format(
1167 language, os.path.basename(filename)))
1168 elif status == WorkerStatusFinished:
1169 self.__showMessage(self.trUtf8("Preparation of '{0}' APIs finished.").format(
1170 language))
1171 elif status == WorkerStatusAborted:
1172 self.__showMessage(self.trUtf8("Preparation of '{0}' APIs cancelled.").format(
1173 language))

eric ide

mercurial