src/eric7/PluginManager/PluginManager.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9299
e40589582f82
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
16 import datetime 16 import datetime
17 import pathlib 17 import pathlib
18 18
19 from PyQt6.QtCore import pyqtSignal, QObject, QFile, QUrl, QIODevice 19 from PyQt6.QtCore import pyqtSignal, QObject, QFile, QUrl, QIODevice
20 from PyQt6.QtGui import QPixmap 20 from PyQt6.QtGui import QPixmap
21 from PyQt6.QtNetwork import ( 21 from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
22 QNetworkAccessManager, QNetworkRequest, QNetworkReply
23 )
24 22
25 from EricWidgets import EricMessageBox 23 from EricWidgets import EricMessageBox
26 from EricWidgets.EricApplication import ericApp 24 from EricWidgets.EricApplication import ericApp
27 25
28 from EricNetwork.EricNetworkProxyFactory import proxyAuthenticationRequired 26 from EricNetwork.EricNetworkProxyFactory import proxyAuthenticationRequired
27
29 try: 28 try:
30 from EricNetwork.EricSslErrorHandler import ( 29 from EricNetwork.EricSslErrorHandler import EricSslErrorHandler, EricSslErrorState
31 EricSslErrorHandler, EricSslErrorState 30
32 )
33 SSL_AVAILABLE = True 31 SSL_AVAILABLE = True
34 except ImportError: 32 except ImportError:
35 SSL_AVAILABLE = False 33 SSL_AVAILABLE = False
36 34
37 from .PluginExceptions import ( 35 from .PluginExceptions import (
38 PluginPathError, PluginModulesError, PluginLoadError, 36 PluginPathError,
39 PluginActivationError, PluginModuleFormatError, PluginClassFormatError 37 PluginModulesError,
38 PluginLoadError,
39 PluginActivationError,
40 PluginModuleFormatError,
41 PluginClassFormatError,
40 ) 42 )
41 43
42 import UI.PixmapCache 44 import UI.PixmapCache
43 45
44 import Globals 46 import Globals
49 51
50 52
51 class PluginManager(QObject): 53 class PluginManager(QObject):
52 """ 54 """
53 Class implementing the Plugin Manager. 55 Class implementing the Plugin Manager.
54 56
55 @signal shutdown() emitted at shutdown of the IDE 57 @signal shutdown() emitted at shutdown of the IDE
56 @signal pluginAboutToBeActivated(modulName, pluginObject) emitted just 58 @signal pluginAboutToBeActivated(modulName, pluginObject) emitted just
57 before a plugin is activated 59 before a plugin is activated
58 @signal pluginActivated(moduleName, pluginObject) emitted just after 60 @signal pluginActivated(moduleName, pluginObject) emitted just after
59 a plugin was activated 61 a plugin was activated
64 @signal pluginDeactivated(moduleName, pluginObject) emitted just after 66 @signal pluginDeactivated(moduleName, pluginObject) emitted just after
65 a plugin was deactivated 67 a plugin was deactivated
66 @signal pluginRepositoryFileDownloaded() emitted to indicate a completed 68 @signal pluginRepositoryFileDownloaded() emitted to indicate a completed
67 download of the plugin repository file 69 download of the plugin repository file
68 """ 70 """
71
69 shutdown = pyqtSignal() 72 shutdown = pyqtSignal()
70 pluginAboutToBeActivated = pyqtSignal(str, object) 73 pluginAboutToBeActivated = pyqtSignal(str, object)
71 pluginActivated = pyqtSignal(str, object) 74 pluginActivated = pyqtSignal(str, object)
72 allPlugginsActivated = pyqtSignal() 75 allPlugginsActivated = pyqtSignal()
73 pluginAboutToBeDeactivated = pyqtSignal(str, object) 76 pluginAboutToBeDeactivated = pyqtSignal(str, object)
74 pluginDeactivated = pyqtSignal(str, object) 77 pluginDeactivated = pyqtSignal(str, object)
75 pluginRepositoryFileDownloaded = pyqtSignal() 78 pluginRepositoryFileDownloaded = pyqtSignal()
76 79
77 def __init__(self, parent=None, disabledPlugins=None, doLoadPlugins=True, 80 def __init__(
78 develPlugin=None): 81 self, parent=None, disabledPlugins=None, doLoadPlugins=True, develPlugin=None
82 ):
79 """ 83 """
80 Constructor 84 Constructor
81 85
82 The Plugin Manager deals with three different plugin directories. 86 The Plugin Manager deals with three different plugin directories.
83 The first is the one, that is part of eric7 (eric7/Plugins). The 87 The first is the one, that is part of eric7 (eric7/Plugins). The
84 second one is the global plugin directory called 'eric7plugins', 88 second one is the global plugin directory called 'eric7plugins',
85 which is located inside the site-packages directory. The last one 89 which is located inside the site-packages directory. The last one
86 is the user plugin directory located inside the .eric7 directory 90 is the user plugin directory located inside the .eric7 directory
87 of the users home directory. 91 of the users home directory.
88 92
89 @param parent reference to the parent object 93 @param parent reference to the parent object
90 @type QObject 94 @type QObject
91 @param disabledPlugins list of plug-ins that have been disabled via 95 @param disabledPlugins list of plug-ins that have been disabled via
92 the command line parameters '--disable-plugin=' 96 the command line parameters '--disable-plugin='
93 @type list of str 97 @type list of str
100 @exception PluginPathError raised to indicate an invalid plug-in path 104 @exception PluginPathError raised to indicate an invalid plug-in path
101 @exception PluginModulesError raised to indicate the absence of 105 @exception PluginModulesError raised to indicate the absence of
102 plug-in modules 106 plug-in modules
103 """ 107 """
104 super().__init__(parent) 108 super().__init__(parent)
105 109
106 self.__ui = parent 110 self.__ui = parent
107 self.__develPluginFile = develPlugin 111 self.__develPluginFile = develPlugin
108 self.__develPluginName = None 112 self.__develPluginName = None
109 if disabledPlugins is not None: 113 if disabledPlugins is not None:
110 self.__disabledPlugins = disabledPlugins[:] 114 self.__disabledPlugins = disabledPlugins[:]
111 else: 115 else:
112 self.__disabledPlugins = [] 116 self.__disabledPlugins = []
113 117
114 self.__inactivePluginsKey = "PluginManager/InactivePlugins" 118 self.__inactivePluginsKey = "PluginManager/InactivePlugins"
115 119
116 self.pluginDirs = { 120 self.pluginDirs = {
117 "eric7": os.path.join(getConfig('ericDir'), "Plugins"), 121 "eric7": os.path.join(getConfig("ericDir"), "Plugins"),
118 "global": os.path.join(Utilities.getPythonLibraryDirectory(), 122 "global": os.path.join(
119 "eric7plugins"), 123 Utilities.getPythonLibraryDirectory(), "eric7plugins"
124 ),
120 "user": os.path.join(Utilities.getConfigDir(), "eric7plugins"), 125 "user": os.path.join(Utilities.getConfigDir(), "eric7plugins"),
121 } 126 }
122 self.__priorityOrder = ["eric7", "global", "user"] 127 self.__priorityOrder = ["eric7", "global", "user"]
123 128
124 self.__defaultDownloadDir = os.path.join( 129 self.__defaultDownloadDir = os.path.join(Utilities.getConfigDir(), "Downloads")
125 Utilities.getConfigDir(), "Downloads") 130
126
127 self.__activePlugins = {} 131 self.__activePlugins = {}
128 self.__inactivePlugins = {} 132 self.__inactivePlugins = {}
129 self.__onDemandActivePlugins = {} 133 self.__onDemandActivePlugins = {}
130 self.__onDemandInactivePlugins = {} 134 self.__onDemandInactivePlugins = {}
131 self.__activeModules = {} 135 self.__activeModules = {}
132 self.__inactiveModules = {} 136 self.__inactiveModules = {}
133 self.__onDemandActiveModules = {} 137 self.__onDemandActiveModules = {}
134 self.__onDemandInactiveModules = {} 138 self.__onDemandInactiveModules = {}
135 self.__failedModules = {} 139 self.__failedModules = {}
136 140
137 self.__foundCoreModules = [] 141 self.__foundCoreModules = []
138 self.__foundGlobalModules = [] 142 self.__foundGlobalModules = []
139 self.__foundUserModules = [] 143 self.__foundUserModules = []
140 144
141 self.__modulesCount = 0 145 self.__modulesCount = 0
142 146
143 pdirsExist, msg = self.__pluginDirectoriesExist() 147 pdirsExist, msg = self.__pluginDirectoriesExist()
144 if not pdirsExist: 148 if not pdirsExist:
145 raise PluginPathError(msg) 149 raise PluginPathError(msg)
146 150
147 if doLoadPlugins: 151 if doLoadPlugins:
148 if not self.__pluginModulesExist(): 152 if not self.__pluginModulesExist():
149 raise PluginModulesError 153 raise PluginModulesError
150 154
151 self.__insertPluginsPaths() 155 self.__insertPluginsPaths()
152 156
153 self.__loadPlugins() 157 self.__loadPlugins()
154 158
155 self.__checkPluginsDownloadDirectory() 159 self.__checkPluginsDownloadDirectory()
156 160
157 self.pluginRepositoryFile = os.path.join(Utilities.getConfigDir(), 161 self.pluginRepositoryFile = os.path.join(
158 "PluginRepository") 162 Utilities.getConfigDir(), "PluginRepository"
159 163 )
164
160 # attributes for the network objects 165 # attributes for the network objects
161 self.__networkManager = QNetworkAccessManager(self) 166 self.__networkManager = QNetworkAccessManager(self)
162 self.__networkManager.proxyAuthenticationRequired.connect( 167 self.__networkManager.proxyAuthenticationRequired.connect(
163 proxyAuthenticationRequired) 168 proxyAuthenticationRequired
169 )
164 if SSL_AVAILABLE: 170 if SSL_AVAILABLE:
165 self.__sslErrorHandler = EricSslErrorHandler(self) 171 self.__sslErrorHandler = EricSslErrorHandler(self)
166 self.__networkManager.sslErrors.connect(self.__sslErrors) 172 self.__networkManager.sslErrors.connect(self.__sslErrors)
167 self.__replies = [] 173 self.__replies = []
168 174
169 def finalizeSetup(self): 175 def finalizeSetup(self):
170 """ 176 """
171 Public method to finalize the setup of the plugin manager. 177 Public method to finalize the setup of the plugin manager.
172 """ 178 """
173 for module in ( 179 for module in list(self.__onDemandInactiveModules.values()) + list(
174 list(self.__onDemandInactiveModules.values()) + 180 self.__onDemandActiveModules.values()
175 list(self.__onDemandActiveModules.values())
176 ): 181 ):
177 if hasattr(module, "moduleSetup"): 182 if hasattr(module, "moduleSetup"):
178 module.moduleSetup() 183 module.moduleSetup()
179 184
180 def getPluginDir(self, key): 185 def getPluginDir(self, key):
181 """ 186 """
182 Public method to get the path of a plugin directory. 187 Public method to get the path of a plugin directory.
183 188
184 @param key key of the plug-in directory (string) 189 @param key key of the plug-in directory (string)
185 @return path of the requested plugin directory (string) 190 @return path of the requested plugin directory (string)
186 """ 191 """
187 if key not in ["global", "user"]: 192 if key not in ["global", "user"]:
188 return None 193 return None
189 else: 194 else:
190 try: 195 try:
191 return self.pluginDirs[key] 196 return self.pluginDirs[key]
192 except KeyError: 197 except KeyError:
193 return None 198 return None
194 199
195 def __pluginDirectoriesExist(self): 200 def __pluginDirectoriesExist(self):
196 """ 201 """
197 Private method to check, if the plugin folders exist. 202 Private method to check, if the plugin folders exist.
198 203
199 If the plugin folders don't exist, they are created (if possible). 204 If the plugin folders don't exist, they are created (if possible).
200 205
201 @return tuple of a flag indicating existence of any of the plugin 206 @return tuple of a flag indicating existence of any of the plugin
202 directories (boolean) and a message (string) 207 directories (boolean) and a message (string)
203 """ 208 """
204 if self.__develPluginFile: 209 if self.__develPluginFile:
205 path = Utilities.splitPath(self.__develPluginFile)[0] 210 path = Utilities.splitPath(self.__develPluginFile)[0]
209 with open(fname, "w"): 214 with open(fname, "w"):
210 pass 215 pass
211 except OSError: 216 except OSError:
212 return ( 217 return (
213 False, 218 False,
214 self.tr("Could not create a package for {0}.") 219 self.tr("Could not create a package for {0}.").format(
215 .format(self.__develPluginFile)) 220 self.__develPluginFile
216 221 ),
222 )
223
217 fname = os.path.join(self.pluginDirs["user"], "__init__.py") 224 fname = os.path.join(self.pluginDirs["user"], "__init__.py")
218 if not os.path.exists(fname): 225 if not os.path.exists(fname):
219 if not os.path.exists(self.pluginDirs["user"]): 226 if not os.path.exists(self.pluginDirs["user"]):
220 os.mkdir(self.pluginDirs["user"], 0o755) 227 os.mkdir(self.pluginDirs["user"], 0o755)
221 try: 228 try:
222 with open(fname, "w"): 229 with open(fname, "w"):
223 pass 230 pass
224 except OSError: 231 except OSError:
225 del self.pluginDirs["user"] 232 del self.pluginDirs["user"]
226 233
227 if not os.path.exists(self.pluginDirs["global"]): 234 if not os.path.exists(self.pluginDirs["global"]):
228 try: 235 try:
229 # create the global plugins directory 236 # create the global plugins directory
230 os.mkdir(self.pluginDirs["global"], 0o755) 237 os.mkdir(self.pluginDirs["global"], 0o755)
231 fname = os.path.join(self.pluginDirs["global"], "__init__.py") 238 fname = os.path.join(self.pluginDirs["global"], "__init__.py")
232 with open(fname, "w", encoding="utf-8") as f: 239 with open(fname, "w", encoding="utf-8") as f:
233 f.write('# -*- coding: utf-8 -*-' + "\n") 240 f.write("# -*- coding: utf-8 -*-" + "\n")
234 f.write("\n") 241 f.write("\n")
235 f.write('"""' + "\n") 242 f.write('"""' + "\n")
236 f.write('Package containing the global plugins.' + "\n") 243 f.write("Package containing the global plugins." + "\n")
237 f.write('"""' + "\n") 244 f.write('"""' + "\n")
238 except OSError: 245 except OSError:
239 del self.pluginDirs["global"] 246 del self.pluginDirs["global"]
240 247
241 if not os.path.exists(self.pluginDirs["eric7"]): 248 if not os.path.exists(self.pluginDirs["eric7"]):
242 return ( 249 return (
243 False, 250 False,
244 self.tr( 251 self.tr(
245 "The internal plugin directory <b>{0}</b>" 252 "The internal plugin directory <b>{0}</b>" " does not exits."
246 " does not exits.").format(self.pluginDirs["eric7"])) 253 ).format(self.pluginDirs["eric7"]),
247 254 )
255
248 return (True, "") 256 return (True, "")
249 257
250 def __pluginModulesExist(self): 258 def __pluginModulesExist(self):
251 """ 259 """
252 Private method to check, if there are plugins available. 260 Private method to check, if there are plugins available.
253 261
254 @return flag indicating the availability of plugins (boolean) 262 @return flag indicating the availability of plugins (boolean)
255 """ 263 """
256 if ( 264 if self.__develPluginFile and not os.path.exists(self.__develPluginFile):
257 self.__develPluginFile and
258 not os.path.exists(self.__develPluginFile)
259 ):
260 return False 265 return False
261 266
262 self.__foundCoreModules = self.getPluginModules( 267 self.__foundCoreModules = self.getPluginModules(self.pluginDirs["eric7"])
263 self.pluginDirs["eric7"])
264 if Preferences.getPluginManager("ActivateExternal"): 268 if Preferences.getPluginManager("ActivateExternal"):
265 if "global" in self.pluginDirs: 269 if "global" in self.pluginDirs:
266 self.__foundGlobalModules = self.getPluginModules( 270 self.__foundGlobalModules = self.getPluginModules(
267 self.pluginDirs["global"]) 271 self.pluginDirs["global"]
272 )
268 if "user" in self.pluginDirs: 273 if "user" in self.pluginDirs:
269 self.__foundUserModules = self.getPluginModules( 274 self.__foundUserModules = self.getPluginModules(self.pluginDirs["user"])
270 self.pluginDirs["user"]) 275
271 276 return (
272 return len(self.__foundCoreModules + self.__foundGlobalModules + 277 len(
273 self.__foundUserModules) > 0 278 self.__foundCoreModules
274 279 + self.__foundGlobalModules
280 + self.__foundUserModules
281 )
282 > 0
283 )
284
275 def getPluginModules(self, pluginPath): 285 def getPluginModules(self, pluginPath):
276 """ 286 """
277 Public method to get a list of plugin modules. 287 Public method to get a list of plugin modules.
278 288
279 @param pluginPath name of the path to search (string) 289 @param pluginPath name of the path to search (string)
280 @return list of plugin module names (list of string) 290 @return list of plugin module names (list of string)
281 """ 291 """
282 pluginFiles = [f[:-3] for f in os.listdir(pluginPath) 292 pluginFiles = [
283 if self.isValidPluginName(f)] 293 f[:-3] for f in os.listdir(pluginPath) if self.isValidPluginName(f)
294 ]
284 return pluginFiles[:] 295 return pluginFiles[:]
285 296
286 def isValidPluginName(self, pluginName): 297 def isValidPluginName(self, pluginName):
287 """ 298 """
288 Public method to check, if a file name is a valid plugin name. 299 Public method to check, if a file name is a valid plugin name.
289 300
290 Plugin modules must start with "Plugin" and have the extension ".py". 301 Plugin modules must start with "Plugin" and have the extension ".py".
291 302
292 @param pluginName name of the file to be checked (string) 303 @param pluginName name of the file to be checked (string)
293 @return flag indicating a valid plugin name (boolean) 304 @return flag indicating a valid plugin name (boolean)
294 """ 305 """
295 return pluginName.startswith("Plugin") and pluginName.endswith(".py") 306 return pluginName.startswith("Plugin") and pluginName.endswith(".py")
296 307
297 def __insertPluginsPaths(self): 308 def __insertPluginsPaths(self):
298 """ 309 """
299 Private method to insert the valid plugin paths intos the search path. 310 Private method to insert the valid plugin paths intos the search path.
300 """ 311 """
301 for key in self.__priorityOrder: 312 for key in self.__priorityOrder:
302 if key in self.pluginDirs: 313 if key in self.pluginDirs:
303 if self.pluginDirs[key] not in sys.path: 314 if self.pluginDirs[key] not in sys.path:
304 sys.path.insert(2, self.pluginDirs[key]) 315 sys.path.insert(2, self.pluginDirs[key])
305 UI.PixmapCache.addSearchPath(self.pluginDirs[key]) 316 UI.PixmapCache.addSearchPath(self.pluginDirs[key])
306 317
307 if self.__develPluginFile: 318 if self.__develPluginFile:
308 path = Utilities.splitPath(self.__develPluginFile)[0] 319 path = Utilities.splitPath(self.__develPluginFile)[0]
309 if path not in sys.path: 320 if path not in sys.path:
310 sys.path.insert(2, path) 321 sys.path.insert(2, path)
311 UI.PixmapCache.addSearchPath(path) 322 UI.PixmapCache.addSearchPath(path)
312 323
313 def __loadPlugins(self): 324 def __loadPlugins(self):
314 """ 325 """
315 Private method to load the plugins found. 326 Private method to load the plugins found.
316 """ 327 """
317 develPluginName = "" 328 develPluginName = ""
318 if self.__develPluginFile: 329 if self.__develPluginFile:
319 develPluginPath, develPluginName = Utilities.splitPath( 330 develPluginPath, develPluginName = Utilities.splitPath(
320 self.__develPluginFile) 331 self.__develPluginFile
332 )
321 if self.isValidPluginName(develPluginName): 333 if self.isValidPluginName(develPluginName):
322 develPluginName = develPluginName[:-3] 334 develPluginName = develPluginName[:-3]
323 335
324 for pluginName in self.__foundGlobalModules: 336 for pluginName in self.__foundGlobalModules:
325 # user and core plug-ins have priority 337 # user and core plug-ins have priority
326 if ( 338 if (
327 pluginName not in self.__foundUserModules and 339 pluginName not in self.__foundUserModules
328 pluginName not in self.__foundCoreModules and 340 and pluginName not in self.__foundCoreModules
329 pluginName != develPluginName 341 and pluginName != develPluginName
330 ): 342 ):
331 self.loadPlugin(pluginName, self.pluginDirs["global"]) 343 self.loadPlugin(pluginName, self.pluginDirs["global"])
332 344
333 for pluginName in self.__foundUserModules: 345 for pluginName in self.__foundUserModules:
334 # core plug-ins have priority 346 # core plug-ins have priority
335 if ( 347 if (
336 pluginName not in self.__foundCoreModules and 348 pluginName not in self.__foundCoreModules
337 pluginName != develPluginName 349 and pluginName != develPluginName
338 ): 350 ):
339 self.loadPlugin(pluginName, self.pluginDirs["user"]) 351 self.loadPlugin(pluginName, self.pluginDirs["user"])
340 352
341 for pluginName in self.__foundCoreModules: 353 for pluginName in self.__foundCoreModules:
342 # plug-in under development has priority 354 # plug-in under development has priority
343 if pluginName != develPluginName: 355 if pluginName != develPluginName:
344 self.loadPlugin(pluginName, self.pluginDirs["eric7"]) 356 self.loadPlugin(pluginName, self.pluginDirs["eric7"])
345 357
346 if develPluginName: 358 if develPluginName:
347 self.loadPlugin(develPluginName, develPluginPath) 359 self.loadPlugin(develPluginName, develPluginPath)
348 self.__develPluginName = develPluginName 360 self.__develPluginName = develPluginName
349 361
350 def loadDocumentationSetPlugins(self): 362 def loadDocumentationSetPlugins(self):
351 """ 363 """
352 Public method to load just the documentation sets plugins. 364 Public method to load just the documentation sets plugins.
353 365
354 @exception PluginModulesError raised to indicate the absence of 366 @exception PluginModulesError raised to indicate the absence of
355 plug-in modules 367 plug-in modules
356 """ 368 """
357 if not self.__pluginModulesExist(): 369 if not self.__pluginModulesExist():
358 raise PluginModulesError 370 raise PluginModulesError
359 371
360 self.__insertPluginsPaths() 372 self.__insertPluginsPaths()
361 373
362 for pluginName in self.__foundGlobalModules: 374 for pluginName in self.__foundGlobalModules:
363 # user and core plug-ins have priority 375 # user and core plug-ins have priority
364 if ( 376 if (
365 pluginName not in self.__foundUserModules and 377 pluginName not in self.__foundUserModules
366 pluginName not in self.__foundCoreModules and 378 and pluginName not in self.__foundCoreModules
367 pluginName.startswith("PluginDocumentationSets") 379 and pluginName.startswith("PluginDocumentationSets")
368 ): 380 ):
369 self.loadPlugin(pluginName, self.pluginDirs["global"]) 381 self.loadPlugin(pluginName, self.pluginDirs["global"])
370 382
371 for pluginName in self.__foundUserModules: 383 for pluginName in self.__foundUserModules:
372 # core plug-ins have priority 384 # core plug-ins have priority
373 if ( 385 if pluginName not in self.__foundCoreModules and pluginName.startswith(
374 pluginName not in self.__foundCoreModules and 386 "PluginDocumentationSets"
375 pluginName.startswith("PluginDocumentationSets")
376 ): 387 ):
377 self.loadPlugin(pluginName, self.pluginDirs["user"]) 388 self.loadPlugin(pluginName, self.pluginDirs["user"])
378 389
379 for pluginName in self.__foundCoreModules: 390 for pluginName in self.__foundCoreModules:
380 # plug-in under development has priority 391 # plug-in under development has priority
381 if pluginName.startswith("PluginDocumentationSets"): 392 if pluginName.startswith("PluginDocumentationSets"):
382 self.loadPlugin(pluginName, self.pluginDirs["eric7"]) 393 self.loadPlugin(pluginName, self.pluginDirs["eric7"])
383 394
384 def loadPlugin(self, name, directory, reload_=False, install=False): 395 def loadPlugin(self, name, directory, reload_=False, install=False):
385 """ 396 """
386 Public method to load a plugin module. 397 Public method to load a plugin module.
387 398
388 Initially all modules are inactive. Modules that are requested on 399 Initially all modules are inactive. Modules that are requested on
389 demand are sorted out and are added to the on demand list. Some 400 demand are sorted out and are added to the on demand list. Some
390 basic validity checks are performed as well. Modules failing these 401 basic validity checks are performed as well. Modules failing these
391 checks are added to the failed modules list. 402 checks are added to the failed modules list.
392 403
393 @param name name of the module to be loaded 404 @param name name of the module to be loaded
394 @type str 405 @type str
395 @param directory name of the plugin directory 406 @param directory name of the plugin directory
396 @type str 407 @type str
397 @param reload_ flag indicating to reload the module 408 @param reload_ flag indicating to reload the module
408 module = importlib.util.module_from_spec(spec) 419 module = importlib.util.module_from_spec(spec)
409 sys.modules[module.__name__] = module 420 sys.modules[module.__name__] = module
410 spec.loader.exec_module(module) 421 spec.loader.exec_module(module)
411 if not hasattr(module, "autoactivate"): 422 if not hasattr(module, "autoactivate"):
412 module.error = self.tr( 423 module.error = self.tr(
413 "Module is missing the 'autoactivate' attribute.") 424 "Module is missing the 'autoactivate' attribute."
425 )
414 self.__failedModules[name] = module 426 self.__failedModules[name] = module
415 raise PluginLoadError(name) 427 raise PluginLoadError(name)
416 if getattr(module, "autoactivate", False): 428 if getattr(module, "autoactivate", False):
417 self.__inactiveModules[name] = module 429 self.__inactiveModules[name] = module
418 else: 430 else:
419 if ( 431 if not hasattr(module, "pluginType") or not hasattr(
420 not hasattr(module, "pluginType") or 432 module, "pluginTypename"
421 not hasattr(module, "pluginTypename")
422 ): 433 ):
423 module.error = self.tr( 434 module.error = self.tr(
424 "Module is missing the 'pluginType' " 435 "Module is missing the 'pluginType' "
425 "and/or 'pluginTypename' attributes." 436 "and/or 'pluginTypename' attributes."
426 ) 437 )
429 else: 440 else:
430 self.__onDemandInactiveModules[name] = module 441 self.__onDemandInactiveModules[name] = module
431 module.eric7PluginModuleName = name 442 module.eric7PluginModuleName = name
432 module.eric7PluginModuleFilename = fname 443 module.eric7PluginModuleFilename = fname
433 if ( 444 if (
434 (install or 445 install or Preferences.getPluginManager("AutoInstallDependencies")
435 Preferences.getPluginManager("AutoInstallDependencies")) and 446 ) and hasattr(module, "installDependencies"):
436 hasattr(module, "installDependencies")
437 ):
438 # ask the module to install its dependencies 447 # ask the module to install its dependencies
439 module.installDependencies(self.pipInstall) 448 module.installDependencies(self.pipInstall)
440 self.__modulesCount += 1 449 self.__modulesCount += 1
441 if reload_: 450 if reload_:
442 importlib.reload(module) 451 importlib.reload(module)
443 self.initOnDemandPlugin(name) 452 self.initOnDemandPlugin(name)
444 with contextlib.suppress(KeyError, AttributeError): 453 with contextlib.suppress(KeyError, AttributeError):
445 pluginObject = self.__onDemandInactivePlugins[name] 454 pluginObject = self.__onDemandInactivePlugins[name]
446 pluginObject.initToolbar( 455 pluginObject.initToolbar(
447 self.__ui, ericApp().getObject("ToolbarManager")) 456 self.__ui, ericApp().getObject("ToolbarManager")
457 )
448 except PluginLoadError: 458 except PluginLoadError:
449 print("Error loading plug-in module:", name) 459 print("Error loading plug-in module:", name)
450 except Exception as err: 460 except Exception as err:
451 module = types.ModuleType(name) 461 module = types.ModuleType(name)
452 module.error = self.tr( 462 module.error = self.tr("Module failed to load. Error: {0}").format(str(err))
453 "Module failed to load. Error: {0}").format(str(err))
454 self.__failedModules[name] = module 463 self.__failedModules[name] = module
455 print("Error loading plug-in module:", name) 464 print("Error loading plug-in module:", name)
456 print(str(err)) 465 print(str(err))
457 466
458 def unloadPlugin(self, name): 467 def unloadPlugin(self, name):
459 """ 468 """
460 Public method to unload a plugin module. 469 Public method to unload a plugin module.
461 470
462 @param name name of the module to be unloaded (string) 471 @param name name of the module to be unloaded (string)
463 @return flag indicating success (boolean) 472 @return flag indicating success (boolean)
464 """ 473 """
465 if name in self.__onDemandActiveModules: 474 if name in self.__onDemandActiveModules:
466 # cannot unload an ondemand plugin, that is in use 475 # cannot unload an ondemand plugin, that is in use
467 return False 476 return False
468 477
469 if name in self.__activeModules: 478 if name in self.__activeModules:
470 self.deactivatePlugin(name) 479 self.deactivatePlugin(name)
471 480
472 if name in self.__inactiveModules: 481 if name in self.__inactiveModules:
473 with contextlib.suppress(KeyError): 482 with contextlib.suppress(KeyError):
474 pluginObject = self.__inactivePlugins[name] 483 pluginObject = self.__inactivePlugins[name]
475 with contextlib.suppress(AttributeError): 484 with contextlib.suppress(AttributeError):
476 pluginObject.prepareUnload() 485 pluginObject.prepareUnload()
483 pluginObject.prepareUnload() 492 pluginObject.prepareUnload()
484 del self.__onDemandInactivePlugins[name] 493 del self.__onDemandInactivePlugins[name]
485 del self.__onDemandInactiveModules[name] 494 del self.__onDemandInactiveModules[name]
486 elif name in self.__failedModules: 495 elif name in self.__failedModules:
487 del self.__failedModules[name] 496 del self.__failedModules[name]
488 497
489 self.__modulesCount -= 1 498 self.__modulesCount -= 1
490 return True 499 return True
491 500
492 def removePluginFromSysModules(self, pluginName, package, 501 def removePluginFromSysModules(self, pluginName, package, internalPackages):
493 internalPackages):
494 """ 502 """
495 Public method to remove a plugin and all related modules from 503 Public method to remove a plugin and all related modules from
496 sys.modules. 504 sys.modules.
497 505
498 @param pluginName name of the plugin module (string) 506 @param pluginName name of the plugin module (string)
499 @param package name of the plugin package (string) 507 @param package name of the plugin package (string)
500 @param internalPackages list of intenal packages (list of string) 508 @param internalPackages list of intenal packages (list of string)
501 @return flag indicating the plugin module was found in sys.modules 509 @return flag indicating the plugin module was found in sys.modules
502 (boolean) 510 (boolean)
504 packages = [package] + internalPackages 512 packages = [package] + internalPackages
505 found = False 513 found = False
506 if not package: 514 if not package:
507 package = "__None__" 515 package = "__None__"
508 for moduleName in list(sys.modules.keys())[:]: 516 for moduleName in list(sys.modules.keys())[:]:
509 if ( 517 if moduleName == pluginName or moduleName.split(".")[0] in packages:
510 moduleName == pluginName or
511 moduleName.split(".")[0] in packages
512 ):
513 found = True 518 found = True
514 del sys.modules[moduleName] 519 del sys.modules[moduleName]
515 return found 520 return found
516 521
517 def initOnDemandPlugins(self): 522 def initOnDemandPlugins(self):
518 """ 523 """
519 Public method to create plugin objects for all on demand plugins. 524 Public method to create plugin objects for all on demand plugins.
520 525
521 Note: The plugins are not activated. 526 Note: The plugins are not activated.
522 """ 527 """
523 names = sorted(self.__onDemandInactiveModules.keys()) 528 names = sorted(self.__onDemandInactiveModules.keys())
524 for name in names: 529 for name in names:
525 self.initOnDemandPlugin(name) 530 self.initOnDemandPlugin(name)
526 531
527 def initOnDemandPlugin(self, name): 532 def initOnDemandPlugin(self, name):
528 """ 533 """
529 Public method to create a plugin object for the named on demand plugin. 534 Public method to create a plugin object for the named on demand plugin.
530 535
531 Note: The plug-in is not activated. 536 Note: The plug-in is not activated.
532 537
533 @param name name of the plug-in (string) 538 @param name name of the plug-in (string)
534 @exception PluginActivationError raised to indicate an issue during the 539 @exception PluginActivationError raised to indicate an issue during the
535 plug-in activation 540 plug-in activation
536 """ 541 """
537 try: 542 try:
538 try: 543 try:
539 module = self.__onDemandInactiveModules[name] 544 module = self.__onDemandInactiveModules[name]
540 except KeyError: 545 except KeyError:
541 return 546 return
542 547
543 if not self.__canActivatePlugin(module): 548 if not self.__canActivatePlugin(module):
544 raise PluginActivationError(module.eric7PluginModuleName) 549 raise PluginActivationError(module.eric7PluginModuleName)
545 version = getattr(module, "version", "0.0.0") 550 version = getattr(module, "version", "0.0.0")
546 className = getattr(module, "className", "") 551 className = getattr(module, "className", "")
547 pluginClass = getattr(module, className) 552 pluginClass = getattr(module, className)
552 pluginObject.eric7PluginName = className 557 pluginObject.eric7PluginName = className
553 pluginObject.eric7PluginVersion = version 558 pluginObject.eric7PluginVersion = version
554 self.__onDemandInactivePlugins[name] = pluginObject 559 self.__onDemandInactivePlugins[name] = pluginObject
555 except PluginActivationError: 560 except PluginActivationError:
556 return 561 return
557 562
558 def initPluginToolbars(self, toolbarManager): 563 def initPluginToolbars(self, toolbarManager):
559 """ 564 """
560 Public method to initialize plug-in toolbars. 565 Public method to initialize plug-in toolbars.
561 566
562 @param toolbarManager reference to the toolbar manager object 567 @param toolbarManager reference to the toolbar manager object
563 (EricToolBarManager) 568 (EricToolBarManager)
564 """ 569 """
565 self.initOnDemandPlugins() 570 self.initOnDemandPlugins()
566 for pluginObject in self.__onDemandInactivePlugins.values(): 571 for pluginObject in self.__onDemandInactivePlugins.values():
567 with contextlib.suppress(AttributeError): 572 with contextlib.suppress(AttributeError):
568 pluginObject.initToolbar(self.__ui, toolbarManager) 573 pluginObject.initToolbar(self.__ui, toolbarManager)
569 574
570 def activatePlugins(self): 575 def activatePlugins(self):
571 """ 576 """
572 Public method to activate all plugins having the "autoactivate" 577 Public method to activate all plugins having the "autoactivate"
573 attribute set to True. 578 attribute set to True.
574 """ 579 """
575 savedInactiveList = Preferences.getSettings().value( 580 savedInactiveList = Preferences.getSettings().value(self.__inactivePluginsKey)
576 self.__inactivePluginsKey)
577 inactiveList = self.__disabledPlugins[:] 581 inactiveList = self.__disabledPlugins[:]
578 if savedInactiveList is not None: 582 if savedInactiveList is not None:
579 inactiveList += [p for p in savedInactiveList 583 inactiveList += [
580 if p not in self.__disabledPlugins] 584 p for p in savedInactiveList if p not in self.__disabledPlugins
585 ]
581 if ( 586 if (
582 self.__develPluginName is not None and 587 self.__develPluginName is not None
583 self.__develPluginName in inactiveList 588 and self.__develPluginName in inactiveList
584 ): 589 ):
585 inactiveList.remove(self.__develPluginName) 590 inactiveList.remove(self.__develPluginName)
586 names = sorted(self.__inactiveModules.keys()) 591 names = sorted(self.__inactiveModules.keys())
587 for name in names: 592 for name in names:
588 if name not in inactiveList: 593 if name not in inactiveList:
589 self.activatePlugin(name) 594 self.activatePlugin(name)
590 self.allPlugginsActivated.emit() 595 self.allPlugginsActivated.emit()
591 596
592 def activatePlugin(self, name, onDemand=False): 597 def activatePlugin(self, name, onDemand=False):
593 """ 598 """
594 Public method to activate a plugin. 599 Public method to activate a plugin.
595 600
596 @param name name of the module to be activated 601 @param name name of the module to be activated
597 @param onDemand flag indicating activation of an 602 @param onDemand flag indicating activation of an
598 on demand plugin (boolean) 603 on demand plugin (boolean)
599 @return reference to the initialized plugin object 604 @return reference to the initialized plugin object
600 @exception PluginActivationError raised to indicate an issue during the 605 @exception PluginActivationError raised to indicate an issue during the
602 """ 607 """
603 try: 608 try:
604 try: 609 try:
605 module = ( 610 module = (
606 self.__onDemandInactiveModules[name] 611 self.__onDemandInactiveModules[name]
607 if onDemand else 612 if onDemand
608 self.__inactiveModules[name] 613 else self.__inactiveModules[name]
609 ) 614 )
610 except KeyError: 615 except KeyError:
611 return None 616 return None
612 617
613 if not self.__canActivatePlugin(module): 618 if not self.__canActivatePlugin(module):
614 raise PluginActivationError(module.eric7PluginModuleName) 619 raise PluginActivationError(module.eric7PluginModuleName)
615 version = getattr(module, "version", "0.0.0") 620 version = getattr(module, "version", "0.0.0")
616 className = getattr(module, "className", "") 621 className = getattr(module, "className", "")
617 pluginClass = getattr(module, className) 622 pluginClass = getattr(module, className)
624 pluginObject = pluginClass(self.__ui) 629 pluginObject = pluginClass(self.__ui)
625 self.pluginAboutToBeActivated.emit(name, pluginObject) 630 self.pluginAboutToBeActivated.emit(name, pluginObject)
626 try: 631 try:
627 obj, ok = pluginObject.activate() 632 obj, ok = pluginObject.activate()
628 except TypeError: 633 except TypeError:
629 module.error = self.tr( 634 module.error = self.tr("Incompatible plugin activation method.")
630 "Incompatible plugin activation method.")
631 obj = None 635 obj = None
632 ok = True 636 ok = True
633 except Exception as err: 637 except Exception as err:
634 module.error = str(err) 638 module.error = str(err)
635 obj = None 639 obj = None
636 ok = False 640 ok = False
637 if not ok: 641 if not ok:
638 return None 642 return None
639 643
640 self.pluginActivated.emit(name, pluginObject) 644 self.pluginActivated.emit(name, pluginObject)
641 pluginObject.eric7PluginModule = module 645 pluginObject.eric7PluginModule = module
642 pluginObject.eric7PluginName = className 646 pluginObject.eric7PluginName = className
643 pluginObject.eric7PluginVersion = version 647 pluginObject.eric7PluginVersion = version
644 648
645 if onDemand: 649 if onDemand:
646 self.__onDemandInactiveModules.pop(name) 650 self.__onDemandInactiveModules.pop(name)
647 with contextlib.suppress(KeyError): 651 with contextlib.suppress(KeyError):
648 self.__onDemandInactivePlugins.pop(name) 652 self.__onDemandInactivePlugins.pop(name)
649 self.__onDemandActivePlugins[name] = pluginObject 653 self.__onDemandActivePlugins[name] = pluginObject
655 self.__activePlugins[name] = pluginObject 659 self.__activePlugins[name] = pluginObject
656 self.__activeModules[name] = module 660 self.__activeModules[name] = module
657 return obj 661 return obj
658 except PluginActivationError: 662 except PluginActivationError:
659 return None 663 return None
660 664
661 def __canActivatePlugin(self, module): 665 def __canActivatePlugin(self, module):
662 """ 666 """
663 Private method to check, if a plugin can be activated. 667 Private method to check, if a plugin can be activated.
664 668
665 @param module reference to the module to be activated 669 @param module reference to the module to be activated
666 @return flag indicating, if the module satisfies all requirements 670 @return flag indicating, if the module satisfies all requirements
667 for being activated (boolean) 671 for being activated (boolean)
668 @exception PluginModuleFormatError raised to indicate an invalid 672 @exception PluginModuleFormatError raised to indicate an invalid
669 plug-in module format 673 plug-in module format
670 @exception PluginClassFormatError raised to indicate an invalid 674 @exception PluginClassFormatError raised to indicate an invalid
671 plug-in class format 675 plug-in class format
672 """ 676 """
673 try: 677 try:
674 if not hasattr(module, "version"): 678 if not hasattr(module, "version"):
675 raise PluginModuleFormatError( 679 raise PluginModuleFormatError(module.eric7PluginModuleName, "version")
676 module.eric7PluginModuleName, "version")
677 if not hasattr(module, "className"): 680 if not hasattr(module, "className"):
678 raise PluginModuleFormatError( 681 raise PluginModuleFormatError(module.eric7PluginModuleName, "className")
679 module.eric7PluginModuleName, "className")
680 className = getattr(module, "className", "") 682 className = getattr(module, "className", "")
681 if not className or not hasattr(module, className): 683 if not className or not hasattr(module, className):
682 raise PluginModuleFormatError( 684 raise PluginModuleFormatError(module.eric7PluginModuleName, className)
683 module.eric7PluginModuleName, className)
684 pluginClass = getattr(module, className) 685 pluginClass = getattr(module, className)
685 if not hasattr(pluginClass, "__init__"): 686 if not hasattr(pluginClass, "__init__"):
686 raise PluginClassFormatError( 687 raise PluginClassFormatError(
687 module.eric7PluginModuleName, 688 module.eric7PluginModuleName, className, "__init__"
688 className, "__init__") 689 )
689 if not hasattr(pluginClass, "activate"): 690 if not hasattr(pluginClass, "activate"):
690 raise PluginClassFormatError( 691 raise PluginClassFormatError(
691 module.eric7PluginModuleName, 692 module.eric7PluginModuleName, className, "activate"
692 className, "activate") 693 )
693 if not hasattr(pluginClass, "deactivate"): 694 if not hasattr(pluginClass, "deactivate"):
694 raise PluginClassFormatError( 695 raise PluginClassFormatError(
695 module.eric7PluginModuleName, 696 module.eric7PluginModuleName, className, "deactivate"
696 className, "deactivate") 697 )
697 return True 698 return True
698 except PluginModuleFormatError as e: 699 except PluginModuleFormatError as e:
699 print(repr(e)) 700 print(repr(e))
700 return False 701 return False
701 except PluginClassFormatError as e: 702 except PluginClassFormatError as e:
702 print(repr(e)) 703 print(repr(e))
703 return False 704 return False
704 705
705 def deactivatePlugin(self, name, onDemand=False): 706 def deactivatePlugin(self, name, onDemand=False):
706 """ 707 """
707 Public method to deactivate a plugin. 708 Public method to deactivate a plugin.
708 709
709 @param name name of the module to be deactivated 710 @param name name of the module to be deactivated
710 @param onDemand flag indicating deactivation of an 711 @param onDemand flag indicating deactivation of an
711 on demand plugin (boolean) 712 on demand plugin (boolean)
712 """ 713 """
713 try: 714 try:
714 module = ( 715 module = (
715 self.__onDemandActiveModules[name] 716 self.__onDemandActiveModules[name]
716 if onDemand else 717 if onDemand
717 self.__activeModules[name] 718 else self.__activeModules[name]
718 ) 719 )
719 except KeyError: 720 except KeyError:
720 return 721 return
721 722
722 if self.__canDeactivatePlugin(module): 723 if self.__canDeactivatePlugin(module):
723 pluginObject = None 724 pluginObject = None
724 if onDemand and name in self.__onDemandActivePlugins: 725 if onDemand and name in self.__onDemandActivePlugins:
725 pluginObject = self.__onDemandActivePlugins[name] 726 pluginObject = self.__onDemandActivePlugins[name]
726 elif not onDemand and name in self.__activePlugins: 727 elif not onDemand and name in self.__activePlugins:
727 pluginObject = self.__activePlugins[name] 728 pluginObject = self.__activePlugins[name]
728 if pluginObject: 729 if pluginObject:
729 self.pluginAboutToBeDeactivated.emit(name, pluginObject) 730 self.pluginAboutToBeDeactivated.emit(name, pluginObject)
730 pluginObject.deactivate() 731 pluginObject.deactivate()
731 self.pluginDeactivated.emit(name, pluginObject) 732 self.pluginDeactivated.emit(name, pluginObject)
732 733
733 if onDemand: 734 if onDemand:
734 self.__onDemandActiveModules.pop(name) 735 self.__onDemandActiveModules.pop(name)
735 self.__onDemandActivePlugins.pop(name) 736 self.__onDemandActivePlugins.pop(name)
736 self.__onDemandInactivePlugins[name] = pluginObject 737 self.__onDemandInactivePlugins[name] = pluginObject
737 self.__onDemandInactiveModules[name] = module 738 self.__onDemandInactiveModules[name] = module
739 self.__activeModules.pop(name) 740 self.__activeModules.pop(name)
740 with contextlib.suppress(KeyError): 741 with contextlib.suppress(KeyError):
741 self.__activePlugins.pop(name) 742 self.__activePlugins.pop(name)
742 self.__inactivePlugins[name] = pluginObject 743 self.__inactivePlugins[name] = pluginObject
743 self.__inactiveModules[name] = module 744 self.__inactiveModules[name] = module
744 745
745 def __canDeactivatePlugin(self, module): 746 def __canDeactivatePlugin(self, module):
746 """ 747 """
747 Private method to check, if a plugin can be deactivated. 748 Private method to check, if a plugin can be deactivated.
748 749
749 @param module reference to the module to be deactivated 750 @param module reference to the module to be deactivated
750 @return flag indicating, if the module satisfies all requirements 751 @return flag indicating, if the module satisfies all requirements
751 for being deactivated (boolean) 752 for being deactivated (boolean)
752 """ 753 """
753 return getattr(module, "deactivateable", True) 754 return getattr(module, "deactivateable", True)
754 755
755 def getPluginObject(self, type_, typename, maybeActive=False): 756 def getPluginObject(self, type_, typename, maybeActive=False):
756 """ 757 """
757 Public method to activate an ondemand plugin given by type and 758 Public method to activate an ondemand plugin given by type and
758 typename. 759 typename.
759 760
760 @param type_ type of the plugin to be activated (string) 761 @param type_ type of the plugin to be activated (string)
761 @param typename name of the plugin within the type category (string) 762 @param typename name of the plugin within the type category (string)
762 @param maybeActive flag indicating, that the plugin may be active 763 @param maybeActive flag indicating, that the plugin may be active
763 already (boolean) 764 already (boolean)
764 @return reference to the initialized plugin object 765 @return reference to the initialized plugin object
765 """ 766 """
766 for name, module in list(self.__onDemandInactiveModules.items()): 767 for name, module in list(self.__onDemandInactiveModules.items()):
767 if ( 768 if (
768 getattr(module, "pluginType", "") == type_ and 769 getattr(module, "pluginType", "") == type_
769 getattr(module, "pluginTypename", "") == typename 770 and getattr(module, "pluginTypename", "") == typename
770 ): 771 ):
771 return self.activatePlugin(name, onDemand=True) 772 return self.activatePlugin(name, onDemand=True)
772 773
773 if maybeActive: 774 if maybeActive:
774 for name, module in list(self.__onDemandActiveModules.items()): 775 for name, module in list(self.__onDemandActiveModules.items()):
775 if ( 776 if (
776 getattr(module, "pluginType", "") == type_ and 777 getattr(module, "pluginType", "") == type_
777 getattr(module, "pluginTypename", "") == typename 778 and getattr(module, "pluginTypename", "") == typename
778 ): 779 ):
779 self.deactivatePlugin(name, onDemand=True) 780 self.deactivatePlugin(name, onDemand=True)
780 return self.activatePlugin(name, onDemand=True) 781 return self.activatePlugin(name, onDemand=True)
781 782
782 return None 783 return None
783 784
784 def getPluginInfos(self): 785 def getPluginInfos(self):
785 """ 786 """
786 Public method to get infos about all loaded plug-ins. 787 Public method to get infos about all loaded plug-ins.
787 788
788 @return list of dictionaries with keys "module_name", "plugin_name", 789 @return list of dictionaries with keys "module_name", "plugin_name",
789 "version", "auto_activate", "active", "short_desc", "error" 790 "version", "auto_activate", "active", "short_desc", "error"
790 @rtype list of dict ("module_name": str, "plugin_name": str, 791 @rtype list of dict ("module_name": str, "plugin_name": str,
791 "version": str, "auto_activate": bool, "active": bool, 792 "version": str, "auto_activate": bool, "active": bool,
792 "short_desc": str, "error": bool) 793 "short_desc": str, "error": bool)
793 """ 794 """
794 infos = [] 795 infos = []
795 796
796 # 1. active, non-on-demand modules 797 # 1. active, non-on-demand modules
797 for name in list(self.__activeModules.keys()): 798 for name in list(self.__activeModules.keys()):
798 info = self.__getShortInfo(self.__activeModules[name]) 799 info = self.__getShortInfo(self.__activeModules[name])
799 info.update({ 800 info.update(
800 "module_name": name, 801 {
801 "auto_activate": True, 802 "module_name": name,
802 "active": True, 803 "auto_activate": True,
803 }) 804 "active": True,
805 }
806 )
804 infos.append(info) 807 infos.append(info)
805 808
806 # 2. inactive, non-on-demand modules 809 # 2. inactive, non-on-demand modules
807 for name in list(self.__inactiveModules.keys()): 810 for name in list(self.__inactiveModules.keys()):
808 info = self.__getShortInfo(self.__inactiveModules[name]) 811 info = self.__getShortInfo(self.__inactiveModules[name])
809 info.update({ 812 info.update(
810 "module_name": name, 813 {
811 "auto_activate": True, 814 "module_name": name,
812 "active": False, 815 "auto_activate": True,
813 }) 816 "active": False,
817 }
818 )
814 infos.append(info) 819 infos.append(info)
815 820
816 # 3. active, on-demand modules 821 # 3. active, on-demand modules
817 for name in list(self.__onDemandActiveModules.keys()): 822 for name in list(self.__onDemandActiveModules.keys()):
818 info = self.__getShortInfo(self.__onDemandActiveModules[name]) 823 info = self.__getShortInfo(self.__onDemandActiveModules[name])
819 info.update({ 824 info.update(
820 "module_name": name, 825 {
821 "auto_activate": False, 826 "module_name": name,
822 "active": True, 827 "auto_activate": False,
823 }) 828 "active": True,
829 }
830 )
824 infos.append(info) 831 infos.append(info)
825 832
826 # 4. inactive, non-on-demand modules 833 # 4. inactive, non-on-demand modules
827 for name in list(self.__onDemandInactiveModules.keys()): 834 for name in list(self.__onDemandInactiveModules.keys()):
828 info = self.__getShortInfo(self.__onDemandInactiveModules[name]) 835 info = self.__getShortInfo(self.__onDemandInactiveModules[name])
829 info.update({ 836 info.update(
830 "module_name": name, 837 {
831 "auto_activate": False, 838 "module_name": name,
832 "active": False, 839 "auto_activate": False,
833 }) 840 "active": False,
841 }
842 )
834 infos.append(info) 843 infos.append(info)
835 844
836 # 5. failed modules 845 # 5. failed modules
837 for name in list(self.__failedModules.keys()): 846 for name in list(self.__failedModules.keys()):
838 info = self.__getShortInfo(self.__failedModules[name]) 847 info = self.__getShortInfo(self.__failedModules[name])
839 info.update({ 848 info.update(
840 "module_name": name, 849 {
841 "auto_activate": False, 850 "module_name": name,
842 "active": False, 851 "auto_activate": False,
843 }) 852 "active": False,
853 }
854 )
844 infos.append(info) 855 infos.append(info)
845 856
846 return infos 857 return infos
847 858
848 def __getShortInfo(self, module): 859 def __getShortInfo(self, module):
849 """ 860 """
850 Private method to extract the short info from a module. 861 Private method to extract the short info from a module.
851 862
852 @param module module to extract short info from 863 @param module module to extract short info from
853 @return dictionay containing plug-in data 864 @return dictionay containing plug-in data
854 @rtype dict ("plugin_name": str, "version": str, "short_desc": str, 865 @rtype dict ("plugin_name": str, "version": str, "short_desc": str,
855 "error": bool) 866 "error": bool)
856 """ 867 """
858 "plugin_name": getattr(module, "name", ""), 869 "plugin_name": getattr(module, "name", ""),
859 "version": getattr(module, "version", ""), 870 "version": getattr(module, "version", ""),
860 "short_desc": getattr(module, "shortDescription", ""), 871 "short_desc": getattr(module, "shortDescription", ""),
861 "error": bool(getattr(module, "error", "")), 872 "error": bool(getattr(module, "error", "")),
862 } 873 }
863 874
864 def getPluginDetails(self, name): 875 def getPluginDetails(self, name):
865 """ 876 """
866 Public method to get detailed information about a plugin. 877 Public method to get detailed information about a plugin.
867 878
868 @param name name of the module to get detailed infos about (string) 879 @param name name of the module to get detailed infos about (string)
869 @return details of the plugin as a dictionary 880 @return details of the plugin as a dictionary
870 """ 881 """
871 details = {} 882 details = {}
872 883
873 autoactivate = True 884 autoactivate = True
874 active = True 885 active = True
875 886
876 if name in self.__activeModules: 887 if name in self.__activeModules:
877 module = self.__activeModules[name] 888 module = self.__activeModules[name]
878 elif name in self.__inactiveModules: 889 elif name in self.__inactiveModules:
879 module = self.__inactiveModules[name] 890 module = self.__inactiveModules[name]
880 active = False 891 active = False
893 # try stripping of a postfix 904 # try stripping of a postfix
894 return self.getPluginDetails(name.rsplit("_", 1)[0]) 905 return self.getPluginDetails(name.rsplit("_", 1)[0])
895 else: 906 else:
896 # should not happen 907 # should not happen
897 return None 908 return None
898 909
899 details["moduleName"] = name 910 details["moduleName"] = name
900 details["moduleFileName"] = getattr( 911 details["moduleFileName"] = getattr(module, "eric7PluginModuleFilename", "")
901 module, "eric7PluginModuleFilename", "")
902 details["pluginName"] = getattr(module, "name", "") 912 details["pluginName"] = getattr(module, "name", "")
903 details["version"] = getattr(module, "version", "") 913 details["version"] = getattr(module, "version", "")
904 details["author"] = getattr(module, "author", "") 914 details["author"] = getattr(module, "author", "")
905 details["description"] = getattr(module, "longDescription", "") 915 details["description"] = getattr(module, "longDescription", "")
906 details["autoactivate"] = autoactivate 916 details["autoactivate"] = autoactivate
907 details["active"] = active 917 details["active"] = active
908 details["error"] = getattr(module, "error", "") 918 details["error"] = getattr(module, "error", "")
909 919
910 return details 920 return details
911 921
912 def doShutdown(self): 922 def doShutdown(self):
913 """ 923 """
914 Public method called to perform actions upon shutdown of the IDE. 924 Public method called to perform actions upon shutdown of the IDE.
915 """ 925 """
916 names = [] 926 names = []
917 for name in list(self.__inactiveModules.keys()): 927 for name in list(self.__inactiveModules.keys()):
918 names.append(name) 928 names.append(name)
919 Preferences.getSettings().setValue(self.__inactivePluginsKey, names) 929 Preferences.getSettings().setValue(self.__inactivePluginsKey, names)
920 930
921 self.shutdown.emit() 931 self.shutdown.emit()
922 932
923 def getPluginDisplayStrings(self, type_): 933 def getPluginDisplayStrings(self, type_):
924 """ 934 """
925 Public method to get the display strings of all plugins of a specific 935 Public method to get the display strings of all plugins of a specific
926 type. 936 type.
927 937
928 @param type_ type of the plugins (string) 938 @param type_ type of the plugins (string)
929 @return dictionary with name as key and display string as value 939 @return dictionary with name as key and display string as value
930 (dictionary of string) 940 (dictionary of string)
931 """ 941 """
932 pluginDict = {} 942 pluginDict = {}
933 943
934 for module in ( 944 for module in list(self.__onDemandActiveModules.values()) + list(
935 list(self.__onDemandActiveModules.values()) + 945 self.__onDemandInactiveModules.values()
936 list(self.__onDemandInactiveModules.values())
937 ): 946 ):
938 if ( 947 if (
939 getattr(module, "pluginType", "") == type_ and 948 getattr(module, "pluginType", "") == type_
940 getattr(module, "error", "") == "" 949 and getattr(module, "error", "") == ""
941 ): 950 ):
942 plugin_name = getattr(module, "pluginTypename", "") 951 plugin_name = getattr(module, "pluginTypename", "")
943 if plugin_name: 952 if plugin_name:
944 if hasattr(module, "displayString"): 953 if hasattr(module, "displayString"):
945 try: 954 try:
948 disp = getattr(module, "displayString", "") 957 disp = getattr(module, "displayString", "")
949 if disp != "": 958 if disp != "":
950 pluginDict[plugin_name] = disp 959 pluginDict[plugin_name] = disp
951 else: 960 else:
952 pluginDict[plugin_name] = plugin_name 961 pluginDict[plugin_name] = plugin_name
953 962
954 return pluginDict 963 return pluginDict
955 964
956 def getPluginPreviewPixmap(self, type_, name): 965 def getPluginPreviewPixmap(self, type_, name):
957 """ 966 """
958 Public method to get a preview pixmap of a plugin of a specific type. 967 Public method to get a preview pixmap of a plugin of a specific type.
959 968
960 @param type_ type of the plugin (string) 969 @param type_ type of the plugin (string)
961 @param name name of the plugin type (string) 970 @param name name of the plugin type (string)
962 @return preview pixmap (QPixmap) 971 @return preview pixmap (QPixmap)
963 """ 972 """
964 for module in ( 973 for module in list(self.__onDemandActiveModules.values()) + list(
965 list(self.__onDemandActiveModules.values()) + 974 self.__onDemandInactiveModules.values()
966 list(self.__onDemandInactiveModules.values())
967 ): 975 ):
968 if ( 976 if (
969 getattr(module, "pluginType", "") == type_ and 977 getattr(module, "pluginType", "") == type_
970 getattr(module, "pluginTypename", "") == name 978 and getattr(module, "pluginTypename", "") == name
971 ): 979 ):
972 if hasattr(module, "previewPix"): 980 if hasattr(module, "previewPix"):
973 return module.previewPix() 981 return module.previewPix()
974 else: 982 else:
975 return QPixmap() 983 return QPixmap()
976 984
977 return QPixmap() 985 return QPixmap()
978 986
979 def getPluginApiFiles(self, language): 987 def getPluginApiFiles(self, language):
980 """ 988 """
981 Public method to get the list of API files installed by a plugin. 989 Public method to get the list of API files installed by a plugin.
982 990
983 @param language language of the requested API files (string) 991 @param language language of the requested API files (string)
984 @return list of API filenames (list of string) 992 @return list of API filenames (list of string)
985 """ 993 """
986 apis = [] 994 apis = []
987 995
988 for module in ( 996 for module in list(self.__activeModules.values()) + list(
989 list(self.__activeModules.values()) + 997 self.__onDemandActiveModules.values()
990 list(self.__onDemandActiveModules.values())
991 ): 998 ):
992 if hasattr(module, "apiFiles"): 999 if hasattr(module, "apiFiles"):
993 apis.extend(module.apiFiles(language)) 1000 apis.extend(module.apiFiles(language))
994 1001
995 return apis 1002 return apis
996 1003
997 def getPluginQtHelpFiles(self): 1004 def getPluginQtHelpFiles(self):
998 """ 1005 """
999 Public method to get the list of QtHelp documentation files provided 1006 Public method to get the list of QtHelp documentation files provided
1000 by a plug-in. 1007 by a plug-in.
1001 1008
1002 @return dictionary with documentation type as key and list of files 1009 @return dictionary with documentation type as key and list of files
1003 as value 1010 as value
1004 @rtype dict (key: str, value: list of str) 1011 @rtype dict (key: str, value: list of str)
1005 """ 1012 """
1006 helpFiles = {} 1013 helpFiles = {}
1007 for module in ( 1014 for module in list(self.__activeModules.values()) + list(
1008 list(self.__activeModules.values()) + 1015 self.__onDemandActiveModules.values()
1009 list(self.__onDemandActiveModules.values())
1010 ): 1016 ):
1011 if hasattr(module, "helpFiles"): 1017 if hasattr(module, "helpFiles"):
1012 helpFiles.update(module.helpFiles()) 1018 helpFiles.update(module.helpFiles())
1013 1019
1014 return helpFiles 1020 return helpFiles
1015 1021
1016 def getPluginExeDisplayData(self): 1022 def getPluginExeDisplayData(self):
1017 """ 1023 """
1018 Public method to get data to display information about a plugins 1024 Public method to get data to display information about a plugins
1019 external tool. 1025 external tool.
1020 1026
1021 @return list of dictionaries containing the data. Each dictionary must 1027 @return list of dictionaries containing the data. Each dictionary must
1022 either contain data for the determination or the data to be 1028 either contain data for the determination or the data to be
1023 displayed.<br /> 1029 displayed.<br />
1024 A dictionary of the first form must have the following entries: 1030 A dictionary of the first form must have the following entries:
1025 <ul> 1031 <ul>
1046 <li>text - entry text to be shown (string)</li> 1052 <li>text - entry text to be shown (string)</li>
1047 <li>version - version text to be shown (string)</li> 1053 <li>version - version text to be shown (string)</li>
1048 </ul> 1054 </ul>
1049 """ 1055 """
1050 infos = [] 1056 infos = []
1051 1057
1052 for module in ( 1058 for module in list(self.__activeModules.values()) + list(
1053 list(self.__activeModules.values()) + 1059 self.__inactiveModules.values()
1054 list(self.__inactiveModules.values())
1055 ): 1060 ):
1056 if hasattr(module, "exeDisplayDataList"): 1061 if hasattr(module, "exeDisplayDataList"):
1057 infos.extend(module.exeDisplayDataList()) 1062 infos.extend(module.exeDisplayDataList())
1058 elif hasattr(module, "exeDisplayData"): 1063 elif hasattr(module, "exeDisplayData"):
1059 infos.append(module.exeDisplayData()) 1064 infos.append(module.exeDisplayData())
1060 for module in ( 1065 for module in list(self.__onDemandActiveModules.values()) + list(
1061 list(self.__onDemandActiveModules.values()) + 1066 self.__onDemandInactiveModules.values()
1062 list(self.__onDemandInactiveModules.values())
1063 ): 1067 ):
1064 if hasattr(module, "exeDisplayDataList"): 1068 if hasattr(module, "exeDisplayDataList"):
1065 infos.extend(module.exeDisplayDataList()) 1069 infos.extend(module.exeDisplayDataList())
1066 elif hasattr(module, "exeDisplayData"): 1070 elif hasattr(module, "exeDisplayData"):
1067 infos.append(module.exeDisplayData()) 1071 infos.append(module.exeDisplayData())
1068 1072
1069 return infos 1073 return infos
1070 1074
1071 def getPluginConfigData(self): 1075 def getPluginConfigData(self):
1072 """ 1076 """
1073 Public method to get the config data of all active, non on-demand 1077 Public method to get the config data of all active, non on-demand
1074 plugins used by the configuration dialog. 1078 plugins used by the configuration dialog.
1075 1079
1076 Plugins supporting this functionality must provide the plugin module 1080 Plugins supporting this functionality must provide the plugin module
1077 function 'getConfigData' returning a dictionary with unique keys 1081 function 'getConfigData' returning a dictionary with unique keys
1078 of lists with the following list contents: 1082 of lists with the following list contents:
1079 <dl> 1083 <dl>
1080 <dt>display string</dt> 1084 <dt>display string</dt>
1095 toplevel entry.</dd> 1099 toplevel entry.</dd>
1096 <dt>reference to configuration page</dt> 1100 <dt>reference to configuration page</dt>
1097 <dd>This will be used by the configuration dialog and must always 1101 <dd>This will be used by the configuration dialog and must always
1098 be None</dd> 1102 be None</dd>
1099 </dl> 1103 </dl>
1100 1104
1101 @return plug-in configuration data 1105 @return plug-in configuration data
1102 """ 1106 """
1103 configData = {} 1107 configData = {}
1104 for module in ( 1108 for module in (
1105 list(self.__activeModules.values()) + 1109 list(self.__activeModules.values())
1106 list(self.__onDemandActiveModules.values()) + 1110 + list(self.__onDemandActiveModules.values())
1107 list(self.__onDemandInactiveModules.values()) 1111 + list(self.__onDemandInactiveModules.values())
1108 ): 1112 ):
1109 if hasattr(module, 'getConfigData'): 1113 if hasattr(module, "getConfigData"):
1110 configData.update(module.getConfigData()) 1114 configData.update(module.getConfigData())
1111 return configData 1115 return configData
1112 1116
1113 def isPluginLoaded(self, pluginName): 1117 def isPluginLoaded(self, pluginName):
1114 """ 1118 """
1115 Public method to check, if a certain plugin is loaded. 1119 Public method to check, if a certain plugin is loaded.
1116 1120
1117 @param pluginName name of the plugin to check for (string) 1121 @param pluginName name of the plugin to check for (string)
1118 @return flag indicating, if the plugin is loaded (boolean) 1122 @return flag indicating, if the plugin is loaded (boolean)
1119 """ 1123 """
1120 return ( 1124 return (
1121 pluginName in self.__activeModules or 1125 pluginName in self.__activeModules
1122 pluginName in self.__inactiveModules or 1126 or pluginName in self.__inactiveModules
1123 pluginName in self.__onDemandActiveModules or 1127 or pluginName in self.__onDemandActiveModules
1124 pluginName in self.__onDemandInactiveModules 1128 or pluginName in self.__onDemandInactiveModules
1125 ) 1129 )
1126 1130
1127 def isPluginActive(self, pluginName): 1131 def isPluginActive(self, pluginName):
1128 """ 1132 """
1129 Public method to check, if a certain plugin is active. 1133 Public method to check, if a certain plugin is active.
1130 1134
1131 @param pluginName name of the plugin to check for (string) 1135 @param pluginName name of the plugin to check for (string)
1132 @return flag indicating, if the plugin is active (boolean) 1136 @return flag indicating, if the plugin is active (boolean)
1133 """ 1137 """
1134 return ( 1138 return (
1135 pluginName in self.__activeModules or 1139 pluginName in self.__activeModules
1136 pluginName in self.__onDemandActiveModules 1140 or pluginName in self.__onDemandActiveModules
1137 ) 1141 )
1138 1142
1139 ########################################################################### 1143 ###########################################################################
1140 ## Specialized plug-in module handling methods below 1144 ## Specialized plug-in module handling methods below
1141 ########################################################################### 1145 ###########################################################################
1142 1146
1143 ########################################################################### 1147 ###########################################################################
1144 ## VCS related methods below 1148 ## VCS related methods below
1145 ########################################################################### 1149 ###########################################################################
1146 1150
1147 def getVcsSystemIndicators(self): 1151 def getVcsSystemIndicators(self):
1148 """ 1152 """
1149 Public method to get the Vcs System indicators. 1153 Public method to get the Vcs System indicators.
1150 1154
1151 Plugins supporting this functionality must support the module function 1155 Plugins supporting this functionality must support the module function
1152 getVcsSystemIndicator returning a dictionary with indicator as key and 1156 getVcsSystemIndicator returning a dictionary with indicator as key and
1153 a tuple with the vcs name (string) and vcs display string (string). 1157 a tuple with the vcs name (string) and vcs display string (string).
1154 1158
1155 @return dictionary with indicator as key and a list of tuples as 1159 @return dictionary with indicator as key and a list of tuples as
1156 values. Each tuple contains the vcs name (string) and vcs display 1160 values. Each tuple contains the vcs name (string) and vcs display
1157 string (string). 1161 string (string).
1158 """ 1162 """
1159 vcsDict = {} 1163 vcsDict = {}
1160 1164
1161 for module in ( 1165 for module in list(self.__onDemandActiveModules.values()) + list(
1162 list(self.__onDemandActiveModules.values()) + 1166 self.__onDemandInactiveModules.values()
1163 list(self.__onDemandInactiveModules.values())
1164 ): 1167 ):
1165 if ( 1168 if getattr(module, "pluginType", "") == "version_control" and hasattr(
1166 getattr(module, "pluginType", "") == "version_control" and 1169 module, "getVcsSystemIndicator"
1167 hasattr(module, "getVcsSystemIndicator")
1168 ): 1170 ):
1169 res = module.getVcsSystemIndicator() 1171 res = module.getVcsSystemIndicator()
1170 for indicator, vcsData in list(res.items()): 1172 for indicator, vcsData in list(res.items()):
1171 if indicator in vcsDict: 1173 if indicator in vcsDict:
1172 vcsDict[indicator].append(vcsData) 1174 vcsDict[indicator].append(vcsData)
1173 else: 1175 else:
1174 vcsDict[indicator] = [vcsData] 1176 vcsDict[indicator] = [vcsData]
1175 1177
1176 return vcsDict 1178 return vcsDict
1177 1179
1178 def deactivateVcsPlugins(self): 1180 def deactivateVcsPlugins(self):
1179 """ 1181 """
1180 Public method to deactivated all activated VCS plugins. 1182 Public method to deactivated all activated VCS plugins.
1181 """ 1183 """
1182 for name, module in list(self.__onDemandActiveModules.items()): 1184 for name, module in list(self.__onDemandActiveModules.items()):
1183 if getattr(module, "pluginType", "") == "version_control": 1185 if getattr(module, "pluginType", "") == "version_control":
1184 self.deactivatePlugin(name, True) 1186 self.deactivatePlugin(name, True)
1185 1187
1186 ######################################################################## 1188 ########################################################################
1187 ## Methods for the creation of the plug-ins download directory 1189 ## Methods for the creation of the plug-ins download directory
1188 ######################################################################## 1190 ########################################################################
1189 1191
1190 def __checkPluginsDownloadDirectory(self): 1192 def __checkPluginsDownloadDirectory(self):
1191 """ 1193 """
1192 Private slot to check for the existence of the plugins download 1194 Private slot to check for the existence of the plugins download
1193 directory. 1195 directory.
1194 """ 1196 """
1195 downloadDir = Preferences.getPluginManager("DownloadPath") 1197 downloadDir = Preferences.getPluginManager("DownloadPath")
1196 if not downloadDir: 1198 if not downloadDir:
1197 downloadDir = self.__defaultDownloadDir 1199 downloadDir = self.__defaultDownloadDir
1198 1200
1199 if not os.path.exists(downloadDir): 1201 if not os.path.exists(downloadDir):
1200 try: 1202 try:
1201 os.mkdir(downloadDir, 0o755) 1203 os.mkdir(downloadDir, 0o755)
1202 except OSError: 1204 except OSError:
1203 # try again with (possibly) new default 1205 # try again with (possibly) new default
1211 self.tr("Plugin Manager Error"), 1213 self.tr("Plugin Manager Error"),
1212 self.tr( 1214 self.tr(
1213 """<p>The plugin download directory""" 1215 """<p>The plugin download directory"""
1214 """ <b>{0}</b> could not be created. Please""" 1216 """ <b>{0}</b> could not be created. Please"""
1215 """ configure it via the configuration""" 1217 """ configure it via the configuration"""
1216 """ dialog.</p><p>Reason: {1}</p>""") 1218 """ dialog.</p><p>Reason: {1}</p>"""
1217 .format(downloadDir, str(err))) 1219 ).format(downloadDir, str(err)),
1220 )
1218 downloadDir = "" 1221 downloadDir = ""
1219 1222
1220 Preferences.setPluginManager("DownloadPath", downloadDir) 1223 Preferences.setPluginManager("DownloadPath", downloadDir)
1221 1224
1222 def preferencesChanged(self): 1225 def preferencesChanged(self):
1223 """ 1226 """
1224 Public slot to react to changes in configuration. 1227 Public slot to react to changes in configuration.
1225 """ 1228 """
1226 self.__checkPluginsDownloadDirectory() 1229 self.__checkPluginsDownloadDirectory()
1227 1230
1228 ######################################################################## 1231 ########################################################################
1229 ## Methods for automatic plug-in update check below 1232 ## Methods for automatic plug-in update check below
1230 ######################################################################## 1233 ########################################################################
1231 1234
1232 def checkPluginUpdatesAvailable(self): 1235 def checkPluginUpdatesAvailable(self):
1233 """ 1236 """
1234 Public method to check the availability of updates of plug-ins. 1237 Public method to check the availability of updates of plug-ins.
1235 """ 1238 """
1236 period = Preferences.getPluginManager("UpdatesCheckInterval") 1239 period = Preferences.getPluginManager("UpdatesCheckInterval")
1237 # 0 = off 1240 # 0 = off
1238 # 1 = daily 1241 # 1 = daily
1239 # 2 = weekly 1242 # 2 = weekly
1240 # 3 = monthly 1243 # 3 = monthly
1241 # 4 = always 1244 # 4 = always
1242 1245
1243 if ( 1246 if period == 0 or (self.__ui is not None and not self.__ui.isOnline()):
1244 period == 0 or
1245 (self.__ui is not None and not self.__ui.isOnline())
1246 ):
1247 return 1247 return
1248 1248
1249 elif period in [1, 2, 3] and pathlib.Path(self.pluginRepositoryFile).exists(): 1249 elif period in [1, 2, 3] and pathlib.Path(self.pluginRepositoryFile).exists():
1250 lastModified = datetime.datetime.fromtimestamp( 1250 lastModified = datetime.datetime.fromtimestamp(
1251 pathlib.Path(self.pluginRepositoryFile).stat().st_mtime 1251 pathlib.Path(self.pluginRepositoryFile).stat().st_mtime
1252 ) 1252 )
1253 now = datetime.datetime.now() 1253 now = datetime.datetime.now()
1254 delta = now - lastModified 1254 delta = now - lastModified
1255 if ( 1255 if (
1256 (period == 1 and lastModified.date().day == now.date().day) or 1256 (period == 1 and lastModified.date().day == now.date().day)
1257 (period == 2 and delta.days < 7) or 1257 or (period == 2 and delta.days < 7)
1258 (period == 3 and delta.days < 30) 1258 or (period == 3 and delta.days < 30)
1259 ): 1259 ):
1260 # daily, weekly, monthly 1260 # daily, weekly, monthly
1261 return 1261 return
1262 1262
1263 self.downLoadRepositoryFile() 1263 self.downLoadRepositoryFile()
1264 1264
1265 def downLoadRepositoryFile(self, url=None): 1265 def downLoadRepositoryFile(self, url=None):
1266 """ 1266 """
1267 Public method to download the plugin repository file. 1267 Public method to download the plugin repository file.
1268 1268
1269 @param url URL to get the plugin repository file from 1269 @param url URL to get the plugin repository file from
1270 (defaults to None) 1270 (defaults to None)
1271 @type QUrl or str (optional) 1271 @type QUrl or str (optional)
1272 """ 1272 """
1273 self.__updateAvailable = False 1273 self.__updateAvailable = False
1274 1274
1275 if url is None: 1275 if url is None:
1276 url = Preferences.getUI("PluginRepositoryUrl7") 1276 url = Preferences.getUI("PluginRepositoryUrl7")
1277 request = QNetworkRequest(QUrl(url)) 1277 request = QNetworkRequest(QUrl(url))
1278 request.setAttribute( 1278 request.setAttribute(
1279 QNetworkRequest.Attribute.CacheLoadControlAttribute, 1279 QNetworkRequest.Attribute.CacheLoadControlAttribute,
1280 QNetworkRequest.CacheLoadControl.AlwaysNetwork) 1280 QNetworkRequest.CacheLoadControl.AlwaysNetwork,
1281 )
1281 reply = self.__networkManager.get(request) 1282 reply = self.__networkManager.get(request)
1282 reply.finished.connect( 1283 reply.finished.connect(lambda: self.__downloadRepositoryFileDone(reply))
1283 lambda: self.__downloadRepositoryFileDone(reply))
1284 self.__replies.append(reply) 1284 self.__replies.append(reply)
1285 1285
1286 def __downloadRepositoryFileDone(self, reply): 1286 def __downloadRepositoryFileDone(self, reply):
1287 """ 1287 """
1288 Private method called after the repository file was downloaded. 1288 Private method called after the repository file was downloaded.
1289 1289
1290 @param reply reference to the reply object of the download 1290 @param reply reference to the reply object of the download
1291 @type QNetworkReply 1291 @type QNetworkReply
1292 """ 1292 """
1293 if reply in self.__replies: 1293 if reply in self.__replies:
1294 self.__replies.remove(reply) 1294 self.__replies.remove(reply)
1295 1295
1296 if reply.error() != QNetworkReply.NetworkError.NoError: 1296 if reply.error() != QNetworkReply.NetworkError.NoError:
1297 EricMessageBox.warning( 1297 EricMessageBox.warning(
1298 None, 1298 None,
1299 self.tr("Error downloading file"), 1299 self.tr("Error downloading file"),
1300 self.tr( 1300 self.tr(
1301 """<p>Could not download the requested file""" 1301 """<p>Could not download the requested file"""
1302 """ from {0}.</p><p>Error: {1}</p>""" 1302 """ from {0}.</p><p>Error: {1}</p>"""
1303 ).format(Preferences.getUI("PluginRepositoryUrl7"), 1303 ).format(
1304 reply.errorString()) 1304 Preferences.getUI("PluginRepositoryUrl7"), reply.errorString()
1305 ),
1305 ) 1306 )
1306 reply.deleteLater() 1307 reply.deleteLater()
1307 return 1308 return
1308 1309
1309 ioDevice = QFile(self.pluginRepositoryFile + ".tmp") 1310 ioDevice = QFile(self.pluginRepositoryFile + ".tmp")
1310 ioDevice.open(QIODevice.OpenModeFlag.WriteOnly) 1311 ioDevice.open(QIODevice.OpenModeFlag.WriteOnly)
1311 ioDevice.write(reply.readAll()) 1312 ioDevice.write(reply.readAll())
1312 ioDevice.close() 1313 ioDevice.close()
1313 if QFile.exists(self.pluginRepositoryFile): 1314 if QFile.exists(self.pluginRepositoryFile):
1314 QFile.remove(self.pluginRepositoryFile) 1315 QFile.remove(self.pluginRepositoryFile)
1315 ioDevice.rename(self.pluginRepositoryFile) 1316 ioDevice.rename(self.pluginRepositoryFile)
1316 reply.deleteLater() 1317 reply.deleteLater()
1317 1318
1318 if os.path.exists(self.pluginRepositoryFile): 1319 if os.path.exists(self.pluginRepositoryFile):
1319 f = QFile(self.pluginRepositoryFile) 1320 f = QFile(self.pluginRepositoryFile)
1320 if f.open(QIODevice.OpenModeFlag.ReadOnly): 1321 if f.open(QIODevice.OpenModeFlag.ReadOnly):
1321 # save current URL 1322 # save current URL
1322 url = Preferences.getUI("PluginRepositoryUrl7") 1323 url = Preferences.getUI("PluginRepositoryUrl7")
1323 1324
1324 # read the repository file 1325 # read the repository file
1325 from EricXML.PluginRepositoryReader import ( 1326 from EricXML.PluginRepositoryReader import PluginRepositoryReader
1326 PluginRepositoryReader 1327
1327 )
1328 reader = PluginRepositoryReader(f, self.checkPluginEntry) 1328 reader = PluginRepositoryReader(f, self.checkPluginEntry)
1329 reader.readXML() 1329 reader.readXML()
1330 if url != Preferences.getUI("PluginRepositoryUrl7"): 1330 if url != Preferences.getUI("PluginRepositoryUrl7"):
1331 # redo if it is a redirect 1331 # redo if it is a redirect
1332 self.checkPluginUpdatesAvailable() 1332 self.checkPluginUpdatesAvailable()
1333 return 1333 return
1334 1334
1335 if self.__updateAvailable: 1335 if self.__updateAvailable:
1336 self.__ui and self.__ui.activatePluginRepositoryViewer() 1336 self.__ui and self.__ui.activatePluginRepositoryViewer()
1337 else: 1337 else:
1338 self.pluginRepositoryFileDownloaded.emit() 1338 self.pluginRepositoryFileDownloaded.emit()
1339 1339
1340 def checkPluginEntry(self, name, short, description, url, author, version, 1340 def checkPluginEntry(
1341 filename, status): 1341 self, name, short, description, url, author, version, filename, status
1342 ):
1342 """ 1343 """
1343 Public method to check a plug-in's data for an update. 1344 Public method to check a plug-in's data for an update.
1344 1345
1345 @param name data for the name field (string) 1346 @param name data for the name field (string)
1346 @param short data for the short field (string) 1347 @param short data for the short field (string)
1347 @param description data for the description field (list of strings) 1348 @param description data for the description field (list of strings)
1348 @param url data for the url field (string) 1349 @param url data for the url field (string)
1349 @param author data for the author field (string) 1350 @param author data for the author field (string)
1353 """ 1354 """
1354 # ignore hidden plug-ins 1355 # ignore hidden plug-ins
1355 pluginName = os.path.splitext(url.rsplit("/", 1)[1])[0] 1356 pluginName = os.path.splitext(url.rsplit("/", 1)[1])[0]
1356 if pluginName in Preferences.getPluginManager("HiddenPlugins"): 1357 if pluginName in Preferences.getPluginManager("HiddenPlugins"):
1357 return 1358 return
1358 1359
1359 archive = os.path.join(Preferences.getPluginManager("DownloadPath"), 1360 archive = os.path.join(Preferences.getPluginManager("DownloadPath"), filename)
1360 filename) 1361
1361
1362 # Check against installed/loaded plug-ins 1362 # Check against installed/loaded plug-ins
1363 pluginDetails = self.getPluginDetails(pluginName) 1363 pluginDetails = self.getPluginDetails(pluginName)
1364 if pluginDetails is None: 1364 if pluginDetails is None:
1365 if not Preferences.getPluginManager("CheckInstalledOnly"): 1365 if not Preferences.getPluginManager("CheckInstalledOnly"):
1366 self.__updateAvailable = True 1366 self.__updateAvailable = True
1367 return 1367 return
1368 1368
1369 versionTuple = Globals.versionToTuple(version)[:3] 1369 versionTuple = Globals.versionToTuple(version)[:3]
1370 pluginVersionTuple = Globals.versionToTuple( 1370 pluginVersionTuple = Globals.versionToTuple(pluginDetails["version"])[:3]
1371 pluginDetails["version"])[:3] 1371
1372
1373 if pluginVersionTuple < versionTuple: 1372 if pluginVersionTuple < versionTuple:
1374 self.__updateAvailable = True 1373 self.__updateAvailable = True
1375 return 1374 return
1376 1375
1377 if not Preferences.getPluginManager("CheckInstalledOnly"): 1376 if not Preferences.getPluginManager("CheckInstalledOnly"):
1378 # Check against downloaded plugin archives 1377 # Check against downloaded plugin archives
1379 # 1. Check, if the archive file exists 1378 # 1. Check, if the archive file exists
1380 if not os.path.exists(archive): 1379 if not os.path.exists(archive):
1381 if pluginDetails["moduleName"] != pluginName: 1380 if pluginDetails["moduleName"] != pluginName:
1382 self.__updateAvailable = True 1381 self.__updateAvailable = True
1383 return 1382 return
1384 1383
1385 # 2. Check, if the archive is a valid zip file 1384 # 2. Check, if the archive is a valid zip file
1386 if not zipfile.is_zipfile(archive): 1385 if not zipfile.is_zipfile(archive):
1387 self.__updateAvailable = True 1386 self.__updateAvailable = True
1388 return 1387 return
1389 1388
1390 # 3. Check the version of the archive file 1389 # 3. Check the version of the archive file
1391 zipFile = zipfile.ZipFile(archive, "r") 1390 zipFile = zipfile.ZipFile(archive, "r")
1392 try: 1391 try:
1393 aversion = zipFile.read("VERSION").decode("utf-8") 1392 aversion = zipFile.read("VERSION").decode("utf-8")
1394 except KeyError: 1393 except KeyError:
1395 aversion = "0.0.0" 1394 aversion = "0.0.0"
1396 zipFile.close() 1395 zipFile.close()
1397 1396
1398 aversionTuple = Globals.versionToTuple(aversion)[:3] 1397 aversionTuple = Globals.versionToTuple(aversion)[:3]
1399 if aversionTuple != versionTuple: 1398 if aversionTuple != versionTuple:
1400 self.__updateAvailable = True 1399 self.__updateAvailable = True
1401 1400
1402 def __sslErrors(self, reply, errors): 1401 def __sslErrors(self, reply, errors):
1403 """ 1402 """
1404 Private slot to handle SSL errors. 1403 Private slot to handle SSL errors.
1405 1404
1406 @param reply reference to the reply object (QNetworkReply) 1405 @param reply reference to the reply object (QNetworkReply)
1407 @param errors list of SSL errors (list of QSslError) 1406 @param errors list of SSL errors (list of QSslError)
1408 """ 1407 """
1409 ignored = self.__sslErrorHandler.sslErrorsReply(reply, errors)[0] 1408 ignored = self.__sslErrorHandler.sslErrorsReply(reply, errors)[0]
1410 if ignored == EricSslErrorState.NOT_IGNORED: 1409 if ignored == EricSslErrorState.NOT_IGNORED:
1411 self.__downloadCancelled = True 1410 self.__downloadCancelled = True
1412 1411
1413 ######################################################################## 1412 ########################################################################
1414 ## Methods to clear private data of plug-ins below 1413 ## Methods to clear private data of plug-ins below
1415 ######################################################################## 1414 ########################################################################
1416 1415
1417 def clearPluginsPrivateData(self, type_): 1416 def clearPluginsPrivateData(self, type_):
1418 """ 1417 """
1419 Public method to clear the private data of plug-ins of a specified 1418 Public method to clear the private data of plug-ins of a specified
1420 type. 1419 type.
1421 1420
1422 Plugins supporting this functionality must support the module function 1421 Plugins supporting this functionality must support the module function
1423 clearPrivateData() and have the module level attribute pluginType. 1422 clearPrivateData() and have the module level attribute pluginType.
1424 1423
1425 @param type_ type of the plugin to clear private data for (string) 1424 @param type_ type of the plugin to clear private data for (string)
1426 """ 1425 """
1427 for module in ( 1426 for module in (
1428 list(self.__onDemandActiveModules.values()) + 1427 list(self.__onDemandActiveModules.values())
1429 list(self.__onDemandInactiveModules.values()) + 1428 + list(self.__onDemandInactiveModules.values())
1430 list(self.__activeModules.values()) + 1429 + list(self.__activeModules.values())
1431 list(self.__inactiveModules.values()) 1430 + list(self.__inactiveModules.values())
1432 ): 1431 ):
1433 if ( 1432 if getattr(module, "pluginType", "") == type_ and hasattr(
1434 getattr(module, "pluginType", "") == type_ and 1433 module, "clearPrivateData"
1435 hasattr(module, "clearPrivateData")
1436 ): 1434 ):
1437 module.clearPrivateData() 1435 module.clearPrivateData()
1438 1436
1439 ######################################################################## 1437 ########################################################################
1440 ## Methods to install a plug-in module dependency via pip 1438 ## Methods to install a plug-in module dependency via pip
1441 ######################################################################## 1439 ########################################################################
1442 1440
1443 def pipInstall(self, packages): 1441 def pipInstall(self, packages):
1444 """ 1442 """
1445 Public method to install the given package via pip. 1443 Public method to install the given package via pip.
1446 1444
1447 @param packages list of packages to install 1445 @param packages list of packages to install
1448 @type list of str 1446 @type list of str
1449 """ 1447 """
1450 try: 1448 try:
1451 pip = ericApp().getObject("Pip") 1449 pip = ericApp().getObject("Pip")
1452 except KeyError: 1450 except KeyError:
1453 # Installation is performed via the plug-in installation script. 1451 # Installation is performed via the plug-in installation script.
1454 from PipInterface.Pip import Pip 1452 from PipInterface.Pip import Pip
1453
1455 pip = Pip(self) 1454 pip = Pip(self)
1456 pip.installPackages(packages, 1455 pip.installPackages(packages, interpreter=Globals.getPythonExecutable())
1457 interpreter=Globals.getPythonExecutable()) 1456
1458 1457
1459 # 1458 #
1460 # eflag: noqa = M801 1459 # eflag: noqa = M801

eric ide

mercurial