AssistantEric/APIsManager.py

changeset 131
7d868e8e1cfb
parent 127
1c7a8660933f
child 135
08cb4f36ad47
equal deleted inserted replaced
130:08c7aece376c 131:7d868e8e1cfb
21 21
22 from E5Gui.E5Application import e5App 22 from E5Gui.E5Application import e5App
23 23
24 import QScintilla.Lexers 24 import QScintilla.Lexers
25 25
26 import Globals
26 import Utilities.ModuleParser 27 import Utilities.ModuleParser
27 import Utilities 28 import Utilities
28 import Preferences 29 import Preferences
29 30
30 WorkerStatusStarted = 2001 31 WorkerStatusStarted = 2001
76 file_delete_id_stmt = """ 77 file_delete_id_stmt = """
77 DELETE FROM file WHERE id = :id 78 DELETE FROM file WHERE id = :id
78 """ 79 """
79 80
80 def __init__(self, proxy, language, apiFiles, projectPath="", 81 def __init__(self, proxy, language, apiFiles, projectPath="",
81 refresh=False): 82 refresh=False, projectType=""):
82 """ 83 """
83 Constructor 84 Constructor
84 85
85 @param proxy reference to the object that is proxied (DbAPIs) 86 @param proxy reference to the object that is proxied
86 @param language language of the APIs object (string) 87 @type DbAPIs
87 @param apiFiles list of API files to process (list of strings) 88 @param language language of the APIs object
89 @type str
90 @param apiFiles list of API files to process
91 @type list of str
88 @param projectPath path of the project. Only needed, if the APIs 92 @param projectPath path of the project. Only needed, if the APIs
89 are extracted out of the sources of a project. (string) 93 are extracted out of the sources of a project.
94 @type str
90 @param refresh flag indicating a refresh of the APIs of one file 95 @param refresh flag indicating a refresh of the APIs of one file
91 (boolean) 96 @type bool
97 @param projectType type of the project
98 @type str
92 """ 99 """
93 QThread.__init__(self) 100 QThread.__init__(self)
94 101
95 self.setTerminationEnabled(True) 102 self.setTerminationEnabled(True)
96 103
103 if lexer is not None: 110 if lexer is not None:
104 self.__wseps[lang] = lexer.autoCompletionWordSeparators() 111 self.__wseps[lang] = lexer.autoCompletionWordSeparators()
105 112
106 self.__proxy = proxy 113 self.__proxy = proxy
107 self.__language = language 114 self.__language = language
115 self.__projectType = projectType
108 self.__apiFiles = apiFiles[:] 116 self.__apiFiles = apiFiles[:]
109 self.__aborted = False 117 self.__aborted = False
110 self.__projectPath = projectPath 118 self.__projectPath = projectPath
111 self.__refresh = refresh 119 self.__refresh = refresh
120
121 if self.__projectType:
122 self.__connectionName = "{0}_{1}".format(
123 self.__language, self.__projectType)
124 else:
125 self.__connectionName = self.__language
112 126
113 def __autoCompletionWordSeparators(self, language): 127 def __autoCompletionWordSeparators(self, language):
114 """ 128 """
115 Private method to get the word separator characters for a language. 129 Private method to get the word separator characters for a language.
116 130
130 Private method to load an API file, if it is newer than the one read 144 Private method to load an API file, if it is newer than the one read
131 into the database. 145 into the database.
132 146
133 @param apiFile filename of the raw API file (string) 147 @param apiFile filename of the raw API file (string)
134 """ 148 """
135 db = QSqlDatabase.database(self.__language) 149 db = QSqlDatabase.database(self.__connectionName)
136 db.transaction() 150 db.transaction()
137 try: 151 try:
138 query = QSqlQuery(db) 152 query = QSqlQuery(db)
139 query.prepare(self.file_loaded_stmt) 153 query.prepare(self.file_loaded_stmt)
140 query.bindValue(":file", apiFile) 154 query.bindValue(":file", apiFile)
247 Doing this avoids rereading the file whenever the API is initialized 261 Doing this avoids rereading the file whenever the API is initialized
248 in case the given file doesn't contain API data. 262 in case the given file doesn't contain API data.
249 263
250 @param apiFile file name of the API file (string) 264 @param apiFile file name of the API file (string)
251 """ 265 """
252 db = QSqlDatabase.database(self.__language) 266 db = QSqlDatabase.database(self.__connectionName)
253 db.transaction() 267 db.transaction()
254 try: 268 try:
255 query = QSqlQuery(db) 269 query = QSqlQuery(db)
256 # step 1: create entry in file table 270 # step 1: create entry in file table
257 query.prepare(self.populate_file_stmt) 271 query.prepare(self.populate_file_stmt)
284 else: 298 else:
285 wseps = self.__proxy.autoCompletionWordSeparators() 299 wseps = self.__proxy.autoCompletionWordSeparators()
286 if wseps is None: 300 if wseps is None:
287 return 301 return
288 302
289 db = QSqlDatabase.database(self.__language) 303 db = QSqlDatabase.database(self.__connectionName)
290 db.transaction() 304 db.transaction()
291 try: 305 try:
292 query = QSqlQuery(db) 306 query = QSqlQuery(db)
293 # step 1: create entry in file table and get the ID 307 # step 1: create entry in file table and get the ID
294 query.prepare(self.populate_file_stmt) 308 query.prepare(self.populate_file_stmt)
408 """ 422 """
409 Private method to delete all references to an api file. 423 Private method to delete all references to an api file.
410 424
411 @param apiFile filename of the raw API file (string) 425 @param apiFile filename of the raw API file (string)
412 """ 426 """
413 db = QSqlDatabase.database(self.__language) 427 db = QSqlDatabase.database(self.__connectionName)
414 db.transaction() 428 db.transaction()
415 try: 429 try:
416 query = QSqlQuery(db) 430 query = QSqlQuery(db)
417 431
418 # step 1: get the ID belonging to the api file 432 # step 1: get the ID belonging to the api file
444 """ 458 """
445 Public method to perform the threads work. 459 Public method to perform the threads work.
446 """ 460 """
447 self.processing.emit(WorkerStatusStarted, "") 461 self.processing.emit(WorkerStatusStarted, "")
448 462
449 db = QSqlDatabase.database(self.__language) 463 db = QSqlDatabase.database(self.__connectionName)
450 if db.isValid() and db.isOpen(): 464 if db.isValid() and db.isOpen():
451 # step 1: remove API files not wanted any longer 465 # step 1: remove API files not wanted any longer
452 if not self.__refresh: 466 if not self.__refresh:
453 loadedApiFiles = self.__proxy.getApiFiles() 467 loadedApiFiles = self.__proxy.getApiFiles()
454 for apiFile in loadedApiFiles: 468 for apiFile in loadedApiFiles:
572 """ 586 """
573 mgmt_insert_stmt = """ 587 mgmt_insert_stmt = """
574 INSERT INTO mgmt (format) VALUES ({0:d}) 588 INSERT INTO mgmt (format) VALUES ({0:d})
575 """.format(DB_VERSION) 589 """.format(DB_VERSION)
576 590
577 def __init__(self, language, parent=None): 591 def __init__(self, language, projectType="", parent=None):
578 """ 592 """
579 Constructor 593 Constructor
580 594
581 @param language language of the APIs object (string) 595 @param language language of the APIs object
582 @param parent reference to the parent object (QObject) 596 @type str
597 @param projectType type of the project
598 @type str
599 @param parent reference to the parent object
600 @type QObject
583 """ 601 """
584 QObject.__init__(self, parent) 602 QObject.__init__(self, parent)
585 self.setObjectName("DbAPIs_{0}".format(language)) 603 if projectType:
604 self.setObjectName("DbAPIs_{0}_{1}".format(language, projectType))
605 else:
606 self.setObjectName("DbAPIs_{0}".format(language))
586 607
587 self.__inPreparation = False 608 self.__inPreparation = False
588 self.__worker = None 609 self.__worker = None
589 self.__workerQueue = [] 610 self.__workerQueue = []
590 self.__opened = False 611 self.__opened = False
591 612
613 self.__projectType = projectType
592 self.__language = language 614 self.__language = language
615
616 if self.__projectType:
617 self.__connectionName = "{0}_{1}".format(
618 self.__language, self.__projectType)
619 else:
620 self.__connectionName = self.__language
621
593 if self.__language == ApisNameProject: 622 if self.__language == ApisNameProject:
594 self.__initAsProject() 623 self.__initAsProject()
595 else: 624 else:
596 self.__initAsLanguage() 625 self.__initAsLanguage()
597 626
618 if self.__language in ["Python", "Python2", "Python3"]: 647 if self.__language in ["Python", "Python2", "Python3"]:
619 self.__discardFirst = ["self", "cls"] 648 self.__discardFirst = ["self", "cls"]
620 else: 649 else:
621 self.__discardFirst = [] 650 self.__discardFirst = []
622 self.__lexer = QScintilla.Lexers.getLexer(self.__language) 651 self.__lexer = QScintilla.Lexers.getLexer(self.__language)
623 self.__apifiles = Preferences.getEditorAPI(self.__language) 652 try:
653 self.__apifiles = Preferences.getEditorAPI(
654 self.__language, projectType=self.__projectType)
655 except TypeError:
656 # older interface
657 self.__apifiles = Preferences.getEditorAPI(self.__language)
624 self.__apifiles.sort() 658 self.__apifiles.sort()
625 if self.__lexer is not None: 659 if self.__lexer is not None:
626 self.__openAPIs() 660 self.__openAPIs()
627 661
628 def _apiDbName(self): 662 def _apiDbName(self):
633 """ 667 """
634 if self.__language == ApisNameProject: 668 if self.__language == ApisNameProject:
635 return os.path.join(self.__project.getProjectManagementDir(), 669 return os.path.join(self.__project.getProjectManagementDir(),
636 "project-apis.db") 670 "project-apis.db")
637 else: 671 else:
638 apiDir = os.path.join(Utilities.getConfigDir(), "APIs") 672 apisDir = os.path.join(Globals.getConfigDir(), "APIs")
639 if not os.path.exists(apiDir): 673 if not os.path.exists(apisDir):
640 os.makedirs(apiDir) 674 os.makedirs(apisDir)
641 return os.path.join(apiDir, "{0}-api.db".format(self.__language)) 675 if self.__projectType:
676 filename = "{0}_{1}-api.db".format(self.__language,
677 self.__projectType)
678 else:
679 filename = "{0}-api.db".format(self.__language)
680 return os.path.join(apisDir, filename)
642 681
643 def close(self): 682 def close(self):
644 """ 683 """
645 Public method to close the database. 684 Public method to close the database.
646 """ 685 """
653 not self.__worker.isFinished(): 692 not self.__worker.isFinished():
654 self.__worker.terminate() 693 self.__worker.terminate()
655 if self.__worker is not None: 694 if self.__worker is not None:
656 self.__worker.wait(5000) 695 self.__worker.wait(5000)
657 696
658 if QSqlDatabase and QSqlDatabase.database(self.__language).isOpen(): 697 if QSqlDatabase and QSqlDatabase.database(
659 QSqlDatabase.database(self.__language).close() 698 self.__connectionName).isOpen():
699 QSqlDatabase.database(self.__connectionName).close()
660 QSqlDatabase.removeDatabase(self.__language) 700 QSqlDatabase.removeDatabase(self.__language)
661 701
662 self.__opened = False 702 self.__opened = False
663 703
664 def __openApiDb(self): 704 def __openApiDb(self):
665 """ 705 """
666 Private method to open the API database. 706 Private method to open the API database.
667 707
668 @return flag indicating the database status (boolean) 708 @return flag indicating the database status (boolean)
669 """ 709 """
670 db = QSqlDatabase.database(self.__language, False) 710 db = QSqlDatabase.database(self.__connectionName, False)
671 if not db.isValid(): 711 if not db.isValid():
672 # the database connection is a new one 712 # the database connection is a new one
673 db = QSqlDatabase.addDatabase("QSQLITE", self.__language) 713 db = QSqlDatabase.addDatabase("QSQLITE", self.__connectionName)
674 dbName = self._apiDbName() 714 dbName = self._apiDbName()
675 if self.__language == ApisNameProject and \ 715 if self.__language == ApisNameProject and \
676 not os.path.exists( 716 not os.path.exists(
677 self.__project.getProjectManagementDir()): 717 self.__project.getProjectManagementDir()):
678 opened = False 718 opened = False
679 else: 719 else:
680 db.setDatabaseName(dbName) 720 db.setDatabaseName(dbName)
681 opened = db.open() 721 opened = db.open()
682 if not opened: 722 if not opened:
683 QSqlDatabase.removeDatabase(self.__language) 723 QSqlDatabase.removeDatabase(self.__connectionName)
684 else: 724 else:
685 opened = True 725 opened = True
686 return opened 726 return opened
687 727
688 def __createApiDB(self): 728 def __createApiDB(self):
689 """ 729 """
690 Private method to create an API database. 730 Private method to create an API database.
691 """ 731 """
692 db = QSqlDatabase.database(self.__language) 732 db = QSqlDatabase.database(self.__connectionName)
693 db.transaction() 733 db.transaction()
694 try: 734 try:
695 query = QSqlQuery(db) 735 query = QSqlQuery(db)
696 # step 1: drop old tables 736 # step 1: drop old tables
697 query.exec_(self.drop_mgmt_stmt) 737 query.exec_(self.drop_mgmt_stmt)
726 766
727 @return list of API filenames (list of strings) 767 @return list of API filenames (list of strings)
728 """ 768 """
729 apiFiles = [] 769 apiFiles = []
730 770
731 db = QSqlDatabase.database(self.__language) 771 db = QSqlDatabase.database(self.__connectionName)
732 db.transaction() 772 db.transaction()
733 try: 773 try:
734 query = QSqlQuery(db) 774 query = QSqlQuery(db)
735 query.exec_(self.api_files_stmt) 775 query.exec_(self.api_files_stmt)
736 while query.next(): 776 while query.next():
745 """ 785 """
746 Private method to check, if the database has been prepared. 786 Private method to check, if the database has been prepared.
747 787
748 @return flag indicating the prepared status (boolean) 788 @return flag indicating the prepared status (boolean)
749 """ 789 """
750 db = QSqlDatabase.database(self.__language) 790 db = QSqlDatabase.database(self.__connectionName)
751 prepared = len(db.tables()) > 0 791 prepared = len(db.tables()) > 0
752 if prepared: 792 if prepared:
753 db.transaction() 793 db.transaction()
754 prepared = False 794 prepared = False
755 try: 795 try:
780 key 'context' contains the context (string) and 820 key 'context' contains the context (string) and
781 key 'pictureId' contains the ID of the icon to be shown (string)) 821 key 'pictureId' contains the ID of the icon to be shown (string))
782 """ 822 """
783 completions = [] 823 completions = []
784 824
785 db = QSqlDatabase.database(self.__language) 825 db = QSqlDatabase.database(self.__connectionName)
786 if db.isOpen() and not self.__inPreparation: 826 if db.isOpen() and not self.__inPreparation:
787 db.transaction() 827 db.transaction()
788 try: 828 try:
789 query = None 829 query = None
790 830
843 base classes (boolean) 883 base classes (boolean)
844 @return list of calltips (list of string) 884 @return list of calltips (list of string)
845 """ 885 """
846 calltips = [] 886 calltips = []
847 887
848 db = QSqlDatabase.database(self.__language) 888 db = QSqlDatabase.database(self.__connectionName)
849 if db.isOpen() and not self.__inPreparation: 889 if db.isOpen() and not self.__inPreparation:
850 if self.autoCompletionWordSeparators(): 890 if self.autoCompletionWordSeparators():
851 contextSeparator = self.autoCompletionWordSeparators()[0] 891 contextSeparator = self.autoCompletionWordSeparators()[0]
852 else: 892 else:
853 contextSeparator = " " 893 contextSeparator = " "
983 apiFiles = self.__project.getSources()[:] 1023 apiFiles = self.__project.getSources()[:]
984 apiFiles.extend( 1024 apiFiles.extend(
985 [f for f in self.__getProjectFormSources() if 1025 [f for f in self.__getProjectFormSources() if
986 f not in apiFiles]) 1026 f not in apiFiles])
987 projectPath = self.__project.getProjectPath() 1027 projectPath = self.__project.getProjectPath()
988 else: 1028 projectType = ""
989 apiFiles = Preferences.getEditorAPI(self.__language) 1029 else:
1030 try:
1031 apiFiles = Preferences.getEditorAPI(
1032 self.__language, projectType=self.__projectType)
1033 except TypeError:
1034 # older interface
1035 apiFiles = Preferences.getEditorAPI(self.__language)
1036 projectType = self.__projectType
990 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, 1037 self.__worker = DbAPIsWorker(self, self.__language, apiFiles,
991 projectPath) 1038 projectPath, projectType=projectType)
992 self.__worker.processing.connect( 1039 self.__worker.processing.connect(
993 self.__processingStatus, Qt.QueuedConnection) 1040 self.__processingStatus, Qt.QueuedConnection)
994 self.__worker.start() 1041 self.__worker.start()
995 1042
996 def __processQueue(self): 1043 def __processQueue(self):
1004 if self.__worker is None and len(self.__workerQueue) > 0: 1051 if self.__worker is None and len(self.__workerQueue) > 0:
1005 apiFiles = [self.__workerQueue.pop(0)] 1052 apiFiles = [self.__workerQueue.pop(0)]
1006 if self.__language == ApisNameProject: 1053 if self.__language == ApisNameProject:
1007 projectPath = self.__project.getProjectPath() 1054 projectPath = self.__project.getProjectPath()
1008 apiFiles = [apiFiles[0].replace(projectPath + os.sep, "")] 1055 apiFiles = [apiFiles[0].replace(projectPath + os.sep, "")]
1056 projectType = ""
1009 else: 1057 else:
1010 projectPath = "" 1058 projectPath = ""
1059 projectType = self.__projectType
1011 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, 1060 self.__worker = DbAPIsWorker(self, self.__language, apiFiles,
1012 projectPath, refresh=True) 1061 projectPath, projectType=projectType,
1062 refresh=True)
1013 self.__worker.processing.connect( 1063 self.__worker.processing.connect(
1014 self.__processingStatus, Qt.QueuedConnection) 1064 self.__processingStatus, Qt.QueuedConnection)
1015 self.__worker.start() 1065 self.__worker.start()
1016 1066
1017 def getLexer(self): 1067 def getLexer(self):
1120 1170
1121 @param mainWindow reference to the main eric6 window (QMainWindow) 1171 @param mainWindow reference to the main eric6 window (QMainWindow)
1122 @param parent reference to the parent object (QObject) 1172 @param parent reference to the parent object (QObject)
1123 """ 1173 """
1124 QObject.__init__(self, parent) 1174 QObject.__init__(self, parent)
1125 self.setObjectName("APIsManager") 1175 self.setObjectName("Assistant_APIsManager")
1126 1176
1127 self.__mw = mainWindow 1177 self.__mw = mainWindow
1128 1178
1129 # initialize the apis dictionary 1179 # initialize the apis dictionary
1130 self.__apis = {} 1180 self.__apis = {}
1134 Public slot to reload the api information. 1184 Public slot to reload the api information.
1135 """ 1185 """
1136 for api in list(self.__apis.values()): 1186 for api in list(self.__apis.values()):
1137 api and api.prepareAPIs() 1187 api and api.prepareAPIs()
1138 1188
1139 def getAPIs(self, language): 1189 def getAPIs(self, language, projectType=""):
1140 """ 1190 """
1141 Public method to get an apis object for autocompletion/calltips. 1191 Public method to get an apis object for autocompletion/calltips.
1142 1192
1143 This method creates and loads an APIs object dynamically upon request. 1193 This method creates and loads an APIs object dynamically upon request.
1144 This saves memory for languages, that might not be needed at the 1194 This saves memory for languages, that might not be needed at the
1145 moment. 1195 moment.
1146 1196
1147 @param language the language of the requested api object (string) 1197 @param language language of the requested APIs object
1148 @return the apis object (APIs) 1198 @type str
1199 @param projectType type of the project
1200 @type str
1201 @return reference to the APIs object
1202 @rtype APIs
1149 """ 1203 """
1150 try: 1204 try:
1151 return self.__apis[language] 1205 return self.__apis[(language, projectType)]
1152 except KeyError: 1206 except KeyError:
1153 if language in QScintilla.Lexers.getSupportedLanguages() or \ 1207 if language in self.__supportedApiLanguages() or \
1154 language == ApisNameProject: 1208 language == ApisNameProject:
1155 # create the api object 1209 # create the api object
1156 api = DbAPIs(language) 1210 api = DbAPIs(language, projectType=projectType)
1157 api.apiPreparationStatus.connect(self.__apiPreparationStatus) 1211 api.apiPreparationStatus.connect(self.__apiPreparationStatus)
1158 self.__apis[language] = api 1212 self.__apis[(language, projectType)] = api
1159 return self.__apis[language] 1213 return self.__apis[(language, projectType)]
1160 else: 1214 else:
1161 return None 1215 return None
1216
1217 def __supportedApiLanguages(self):
1218 """
1219 Private method to build a list of supported API languages.
1220
1221 Note: This is a compatibility method to make this code work with
1222 older eric versions.
1223
1224 @return list of supported API languages
1225 @rtype list of str
1226 """
1227 try:
1228 return QScintilla.Lexers.getSupportedApiLanguages()
1229 except AttributeError:
1230 return [lang for lang in
1231 QScintilla.Lexers.getSupportedLanguages().keys()
1232 if lang != "Guessed" and not lang.startswith("Pygments|")]
1162 1233
1163 def deactivate(self): 1234 def deactivate(self):
1164 """ 1235 """
1165 Public method to perform actions upon deactivation. 1236 Public method to perform actions upon deactivation.
1166 """ 1237 """

eric ide

mercurial