--- a/eric6/PluginManager/PluginManager.py Sat May 01 14:27:38 2021 +0200 +++ b/eric6/PluginManager/PluginManager.py Thu Jun 03 11:39:23 2021 +0200 @@ -13,6 +13,7 @@ import types import importlib import contextlib +import logging from PyQt5.QtCore import ( pyqtSignal, QObject, QDate, QFile, QFileInfo, QUrl, QIODevice @@ -110,7 +111,7 @@ self.pluginDirs = { "eric6": os.path.join(getConfig('ericDir'), "Plugins"), - "global": os.path.join(Utilities.getPythonModulesDirectory(), + "global": os.path.join(Utilities.getPythonLibraryDirectory(), "eric6plugins"), "user": os.path.join(Utilities.getConfigDir(), "eric6plugins"), } @@ -379,7 +380,7 @@ if pluginName.startswith("PluginDocumentationSets"): self.loadPlugin(pluginName, self.pluginDirs["eric6"]) - def loadPlugin(self, name, directory, reload_=False): + def loadPlugin(self, name, directory, reload_=False, install=False): """ Public method to load a plugin module. @@ -388,9 +389,15 @@ basic validity checks are performed as well. Modules failing these checks are added to the failed modules list. - @param name name of the module to be loaded (string) - @param directory name of the plugin directory (string) - @param reload_ flag indicating to reload the module (boolean) + @param name name of the module to be loaded + @type str + @param directory name of the plugin directory + @type str + @param reload_ flag indicating to reload the module + @type bool + @param install flag indicating a load operation as part of an + installation process + @type bool @exception PluginLoadError raised to indicate an issue loading the plug-in """ @@ -403,6 +410,10 @@ if not hasattr(module, "autoactivate"): module.error = self.tr( "Module is missing the 'autoactivate' attribute.") + logging.debug( + "{0}: Module is missing the 'autoactivate' attribute." + .format(name) + ) self.__failedModules[name] = module raise PluginLoadError(name) if getattr(module, "autoactivate", False): @@ -416,12 +427,20 @@ "Module is missing the 'pluginType' " "and/or 'pluginTypename' attributes." ) + logging.debug( + "{0}: Module is missing the 'pluginType' " + "and/or 'pluginTypename' attributes." + .format(name) + ) self.__failedModules[name] = module raise PluginLoadError(name) else: self.__onDemandInactiveModules[name] = module module.eric6PluginModuleName = name module.eric6PluginModuleFilename = fname + if install and hasattr(module, "installDependencies"): + # ask the module to install its dependencies + module.installDependencies(self.pipInstall) self.__modulesCount += 1 if reload_: importlib.reload(module) @@ -436,6 +455,10 @@ module = types.ModuleType(name) module.error = self.tr( "Module failed to load. Error: {0}").format(str(err)) + logging.debug( + "{0}: Module failed to load. Error: {1}" + .format(name, str(err)) + ) self.__failedModules[name] = module print("Error loading plug-in module:", name) print(str(err)) @@ -613,10 +636,15 @@ except TypeError: module.error = self.tr( "Incompatible plugin activation method.") + logging.debug( + "{0}: Incompatible plugin activation method." + .format(name) + ) obj = None ok = True except Exception as err: module.error = str(err) + logging.debug("{0}: {1}".format(name, str(err))) obj = None ok = False if not ok: @@ -1416,6 +1444,25 @@ hasattr(module, "clearPrivateData") ): module.clearPrivateData() + + ######################################################################## + ## Methods to install a plug-in module dependency via pip + ######################################################################## + + def pipInstall(self, packages): + """ + Public method to install the given package via pip. + + @param packages list of packages to install + @type list of str + """ + try: + pip = e5App().getObject("Pip") + except KeyError: + # Installation is performed via the plug-in installation script. + from PipInterface.Pip import Pip + pip = Pip(self) + pip.installPackages(packages, interpreter=sys.executable) # # eflag: noqa = M801