PluginManager/PluginManager.py

branch
Py2 comp.
changeset 3142
55030c09e142
parent 3060
5883ce99ee12
parent 3116
ee0a183cec81
child 3145
a9de05d4a22f
equal deleted inserted replaced
3141:72f3bde98c58 3142:55030c09e142
10 from __future__ import unicode_literals # __IGNORE_WARNING__ 10 from __future__ import unicode_literals # __IGNORE_WARNING__
11 11
12 import os 12 import os
13 import sys 13 import sys
14 import imp 14 import imp
15 15 import zipfile
16 from PyQt4.QtCore import pyqtSignal, QObject 16
17 from PyQt4.QtCore import pyqtSignal, QObject, QDate, QFile, QFileInfo, QUrl, \
18 QIODevice
17 from PyQt4.QtGui import QPixmap 19 from PyQt4.QtGui import QPixmap
20 from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest, \
21 QNetworkReply
18 22
19 from E5Gui import E5MessageBox 23 from E5Gui import E5MessageBox
24
25 from E5Network.E5NetworkProxyFactory import proxyAuthenticationRequired
26 try:
27 from E5Network.E5SslErrorHandler import E5SslErrorHandler
28 SSL_AVAILABLE = True
29 except ImportError:
30 SSL_AVAILABLE = False
20 31
21 from .PluginExceptions import PluginPathError, PluginModulesError, \ 32 from .PluginExceptions import PluginPathError, PluginModulesError, \
22 PluginLoadError, PluginActivationError, PluginModuleFormatError, \ 33 PluginLoadError, PluginActivationError, PluginModuleFormatError, \
23 PluginClassFormatError 34 PluginClassFormatError
24 35
117 self.__insertPluginsPaths() 128 self.__insertPluginsPaths()
118 129
119 self.__loadPlugins() 130 self.__loadPlugins()
120 131
121 self.__checkPluginsDownloadDirectory() 132 self.__checkPluginsDownloadDirectory()
133
134 self.pluginRepositoryFile = \
135 os.path.join(Utilities.getConfigDir(), "PluginRepository")
136
137 # attributes for the network objects
138 self.__networkManager = QNetworkAccessManager(self)
139 self.__networkManager.proxyAuthenticationRequired.connect(
140 proxyAuthenticationRequired)
141 if SSL_AVAILABLE:
142 self.__sslErrorHandler = E5SslErrorHandler(self)
143 self.__networkManager.sslErrors.connect(self.__sslErrors)
144 self.__replies = []
122 145
123 def finalizeSetup(self): 146 def finalizeSetup(self):
124 """ 147 """
125 Public method to finalize the setup of the plugin manager. 148 Public method to finalize the setup of the plugin manager.
126 """ 149 """
168 191
169 if Preferences.getPluginManager("ActivateExternal"): 192 if Preferences.getPluginManager("ActivateExternal"):
170 fname = os.path.join(self.pluginDirs["user"], "__init__.py") 193 fname = os.path.join(self.pluginDirs["user"], "__init__.py")
171 if not os.path.exists(fname): 194 if not os.path.exists(fname):
172 if not os.path.exists(self.pluginDirs["user"]): 195 if not os.path.exists(self.pluginDirs["user"]):
173 os.mkdir(self.pluginDirs["user"], 0o755) 196 os.mkdir(self.pluginDirs["user"], 0o755)
174 try: 197 try:
175 f = open(fname, "w") 198 f = open(fname, "w")
176 f.close() 199 f.close()
177 except IOError: 200 except IOError:
178 del self.pluginDirs["user"] 201 del self.pluginDirs["user"]
341 except Exception as err: 364 except Exception as err:
342 module = imp.new_module(name) 365 module = imp.new_module(name)
343 module.error = self.trUtf8( 366 module.error = self.trUtf8(
344 "Module failed to load. Error: {0}").format(str(err)) 367 "Module failed to load. Error: {0}").format(str(err))
345 self.__failedModules[name] = module 368 self.__failedModules[name] = module
346 print("Error loading plugin module:", name) 369 print("Error loading plugin module:", name)
347 print(str(err)) 370 print(str(err))
348 371
349 def unloadPlugin(self, name): 372 def unloadPlugin(self, name):
350 """ 373 """
351 Public method to unload a plugin module. 374 Public method to unload a plugin module.
660 (boolean), short description (string), error flag (boolean) 683 (boolean), short description (string), error flag (boolean)
661 """ 684 """
662 infos = [] 685 infos = []
663 686
664 for name in list(self.__activeModules.keys()): 687 for name in list(self.__activeModules.keys()):
665 pname, shortDesc, error, version = \ 688 pname, shortDesc, error, version = \
666 self.__getShortInfo(self.__activeModules[name]) 689 self.__getShortInfo(self.__activeModules[name])
667 infos.append((name, pname, version, True, True, shortDesc, error)) 690 infos.append((name, pname, version, True, True, shortDesc, error))
668 for name in list(self.__inactiveModules.keys()): 691 for name in list(self.__inactiveModules.keys()):
669 pname, shortDesc, error, version = \ 692 pname, shortDesc, error, version = \
670 self.__getShortInfo(self.__inactiveModules[name]) 693 self.__getShortInfo(self.__inactiveModules[name])
671 infos.append( 694 infos.append(
672 (name, pname, version, True, False, shortDesc, error)) 695 (name, pname, version, True, False, shortDesc, error))
673 for name in list(self.__onDemandActiveModules.keys()): 696 for name in list(self.__onDemandActiveModules.keys()):
674 pname, shortDesc, error, version = \ 697 pname, shortDesc, error, version = \
675 self.__getShortInfo(self.__onDemandActiveModules[name]) 698 self.__getShortInfo(self.__onDemandActiveModules[name])
676 infos.append( 699 infos.append(
677 (name, pname, version, False, True, shortDesc, error)) 700 (name, pname, version, False, True, shortDesc, error))
678 for name in list(self.__onDemandInactiveModules.keys()): 701 for name in list(self.__onDemandInactiveModules.keys()):
679 pname, shortDesc, error, version = \ 702 pname, shortDesc, error, version = \
680 self.__getShortInfo(self.__onDemandInactiveModules[name]) 703 self.__getShortInfo(self.__onDemandInactiveModules[name])
681 infos.append( 704 infos.append(
682 (name, pname, version, False, False, shortDesc, error)) 705 (name, pname, version, False, False, shortDesc, error))
683 for name in list(self.__failedModules.keys()): 706 for name in list(self.__failedModules.keys()):
684 pname, shortDesc, error, version = \ 707 pname, shortDesc, error, version = \
685 self.__getShortInfo(self.__failedModules[name]) 708 self.__getShortInfo(self.__failedModules[name])
686 infos.append( 709 infos.append(
687 (name, pname, version, False, False, shortDesc, error)) 710 (name, pname, version, False, False, shortDesc, error))
688 return infos 711 return infos
689 712
690 def __getShortInfo(self, module): 713 def __getShortInfo(self, module):
691 """ 714 """
692 Private method to extract the short info from a module. 715 Private method to extract the short info from a module.
1015 def preferencesChanged(self): 1038 def preferencesChanged(self):
1016 """ 1039 """
1017 Public slot to react to changes in configuration. 1040 Public slot to react to changes in configuration.
1018 """ 1041 """
1019 self.__checkPluginsDownloadDirectory() 1042 self.__checkPluginsDownloadDirectory()
1043
1044 ########################################################################
1045 ## Methods for automatic plug-in update check below
1046 ########################################################################
1047
1048 def checkPluginUpdatesAvailable(self):
1049 """
1050 Public method to check the availability of updates of plug-ins.
1051 """
1052 period = Preferences.getPluginManager("UpdatesCheckInterval")
1053 if period == 0:
1054 return
1055 elif period in [2, 3, 4]:
1056 lastModified = QFileInfo(self.pluginRepositoryFile).lastModified()
1057 if lastModified.isValid() and lastModified.date().isValid():
1058 lastModifiedDate = lastModified.date()
1059 now = QDate.currentDate()
1060 if period == 2 and lastModifiedDate.day() == now.day():
1061 # daily
1062 return
1063 elif period == 3 and lastModifiedDate.daysTo(now) < 7:
1064 # weekly
1065 return
1066 elif period == 4 and lastModifiedDate.month() == now.month():
1067 # monthly
1068 return
1069
1070 self.__updateAvailable = False
1071
1072 request = QNetworkRequest(
1073 QUrl(Preferences.getUI("PluginRepositoryUrl5")))
1074 request.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
1075 QNetworkRequest.AlwaysNetwork)
1076 reply = self.__networkManager.get(request)
1077 reply.finished[()].connect(self.__downloadRepositoryFileDone)
1078 self.__replies.append(reply)
1079
1080 def __downloadRepositoryFileDone(self):
1081 """
1082 Private method called after the repository file was downloaded.
1083 """
1084 reply = self.sender()
1085 if reply in self.__replies:
1086 self.__replies.remove(reply)
1087 if reply.error() != QNetworkReply.NoError:
1088 E5MessageBox.warning(
1089 None,
1090 self.trUtf8("Error downloading file"),
1091 self.trUtf8(
1092 """<p>Could not download the requested file"""
1093 """ from {0}.</p><p>Error: {1}</p>"""
1094 ).format(Preferences.getUI("PluginRepositoryUrl5"),
1095 reply.errorString())
1096 )
1097 return
1098
1099 ioDevice = QFile(self.pluginRepositoryFile + ".tmp")
1100 ioDevice.open(QIODevice.WriteOnly)
1101 ioDevice.write(reply.readAll())
1102 ioDevice.close()
1103 if QFile.exists(self.pluginRepositoryFile):
1104 QFile.remove(self.pluginRepositoryFile)
1105 ioDevice.rename(self.pluginRepositoryFile)
1106
1107 if os.path.exists(self.pluginRepositoryFile):
1108 f = QFile(self.pluginRepositoryFile)
1109 if f.open(QIODevice.ReadOnly):
1110 # save current URL
1111 url = Preferences.getUI("PluginRepositoryUrl5")
1112
1113 # read the repository file
1114 from E5XML.PluginRepositoryReader import PluginRepositoryReader
1115 reader = PluginRepositoryReader(f, self.checkPluginEntry)
1116 reader.readXML()
1117 if url != Preferences.getUI("PluginRepositoryUrl5"):
1118 # redo if it is a redirect
1119 self.checkPluginUpdatesAvailable()
1120 return
1121
1122 if self.__updateAvailable:
1123 res = E5MessageBox.information(
1124 None,
1125 self.trUtf8("New plugin versions available"),
1126 self.trUtf8("<p>There are new plug-ins or plug-in"
1127 " updates available. Use the plug-in"
1128 " repository dialog to get them.</p>"),
1129 E5MessageBox.StandardButtons(
1130 E5MessageBox.Ignore | \
1131 E5MessageBox.Open),
1132 E5MessageBox.Open)
1133 if res == E5MessageBox.Open:
1134 self.__ui.showPluginsAvailable()
1135
1136 def checkPluginEntry(self, name, short, description, url, author, version,
1137 filename, status):
1138 """
1139 Public method to check a plug-in's data for an update.
1140
1141 @param name data for the name field (string)
1142 @param short data for the short field (string)
1143 @param description data for the description field (list of strings)
1144 @param url data for the url field (string)
1145 @param author data for the author field (string)
1146 @param version data for the version field (string)
1147 @param filename data for the filename field (string)
1148 @param status status of the plugin (string [stable, unstable, unknown])
1149 """
1150 archive = os.path.join(Preferences.getPluginManager("DownloadPath"),
1151 filename)
1152
1153 # Check against installed/loaded plug-ins
1154 pluginName = os.path.splitext(url.rsplit("/", 1)[1])[0]
1155 pluginDetails = self.getPluginDetails(pluginName)
1156 if pluginDetails is None:
1157 if not Preferences.getPluginManager("CheckInstalledOnly"):
1158 self.__updateAvailable = True
1159 return
1160
1161 if pluginDetails["version"] < version:
1162 self.__updateAvailable = True
1163 return
1164
1165 if not Preferences.getPluginManager("CheckInstalledOnly"):
1166 # Check against downloaded plugin archives
1167 # 1. Check, if the archive file exists
1168 if not os.path.exists(archive):
1169 self.__updateAvailable = True
1170 return
1171
1172 # 2. Check, if the archive is a valid zip file
1173 if not zipfile.is_zipfile(archive):
1174 self.__updateAvailable = True
1175 return
1176
1177 # 3. Check the version of the archive file
1178 zip = zipfile.ZipFile(archive, "r")
1179 try:
1180 aversion = zip.read("VERSION").decode("utf-8")
1181 except KeyError:
1182 aversion = ""
1183 zip.close()
1184
1185 if aversion != version:
1186 self.__updateAvailable = True
1187
1188 def __sslErrors(self, reply, errors):
1189 """
1190 Private slot to handle SSL errors.
1191
1192 @param reply reference to the reply object (QNetworkReply)
1193 @param errors list of SSL errors (list of QSslError)
1194 """
1195 ignored = self.__sslErrorHandler.sslErrorsReply(reply, errors)[0]
1196 if ignored == E5SslErrorHandler.NotIgnored:
1197 self.__downloadCancelled = True

eric ide

mercurial