src/eric7/PluginManager/PluginInstallDialog.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
--- a/src/eric7/PluginManager/PluginInstallDialog.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/PluginManager/PluginInstallDialog.py	Wed Jul 13 14:55:47 2022 +0200
@@ -20,8 +20,12 @@
 
 from PyQt6.QtCore import pyqtSlot, Qt
 from PyQt6.QtWidgets import (
-    QWidget, QDialogButtonBox, QAbstractButton, QApplication, QDialog,
-    QVBoxLayout
+    QWidget,
+    QDialogButtonBox,
+    QAbstractButton,
+    QApplication,
+    QDialog,
+    QVBoxLayout,
 )
 
 from EricWidgets import EricFileDialog
@@ -39,10 +43,11 @@
     """
     Class implementing the Plugin installation dialog.
     """
+
     def __init__(self, pluginManager, pluginFileNames, parent=None):
         """
         Constructor
-        
+
         @param pluginManager reference to the plugin manager object
         @param pluginFileNames list of plugin files suggested for
             installation (list of strings)
@@ -50,45 +55,49 @@
         """
         super().__init__(parent)
         self.setupUi(self)
-        
+
         if pluginManager is None:
             # started as external plugin installer
             from .PluginManager import PluginManager
+
             self.__pluginManager = PluginManager(doLoadPlugins=False)
             self.__external = True
         else:
             self.__pluginManager = pluginManager
             self.__external = False
-        
+
         self.__backButton = self.buttonBox.addButton(
-            self.tr("< Back"), QDialogButtonBox.ButtonRole.ActionRole)
+            self.tr("< Back"), QDialogButtonBox.ButtonRole.ActionRole
+        )
         self.__nextButton = self.buttonBox.addButton(
-            self.tr("Next >"), QDialogButtonBox.ButtonRole.ActionRole)
+            self.tr("Next >"), QDialogButtonBox.ButtonRole.ActionRole
+        )
         self.__finishButton = self.buttonBox.addButton(
-            self.tr("Install"), QDialogButtonBox.ButtonRole.ActionRole)
-        
+            self.tr("Install"), QDialogButtonBox.ButtonRole.ActionRole
+        )
+
         self.__closeButton = self.buttonBox.button(
-            QDialogButtonBox.StandardButton.Close)
+            QDialogButtonBox.StandardButton.Close
+        )
         self.__cancelButton = self.buttonBox.button(
-            QDialogButtonBox.StandardButton.Cancel)
-        
+            QDialogButtonBox.StandardButton.Cancel
+        )
+
         userDir = self.__pluginManager.getPluginDir("user")
         if userDir is not None:
-            self.destinationCombo.addItem(
-                self.tr("User plugins directory"),
-                userDir)
-        
+            self.destinationCombo.addItem(self.tr("User plugins directory"), userDir)
+
         globalDir = self.__pluginManager.getPluginDir("global")
         if globalDir is not None and os.access(globalDir, os.W_OK):
             self.destinationCombo.addItem(
-                self.tr("Global plugins directory"),
-                globalDir)
-        
+                self.tr("Global plugins directory"), globalDir
+            )
+
         self.__installedDirs = []
         self.__installedFiles = []
-        
+
         self.__restartNeeded = False
-        
+
         downloadDir = Preferences.getPluginManager("DownloadPath")
         for pluginFileName in pluginFileNames:
             pluginFilePath = pathlib.Path(pluginFileName)
@@ -96,22 +105,22 @@
                 pluginFilePath = downloadDir / pluginFilePath
             self.archivesList.addItem(str(pluginFilePath))
             self.archivesList.sortItems()
-        
+
         self.__currentIndex = 0
         self.__selectPage()
-    
+
     def restartNeeded(self):
         """
         Public method to check, if a restart of the IDE is required.
-        
+
         @return flag indicating a restart is required (boolean)
         """
         return self.__restartNeeded
-    
+
     def __createArchivesList(self):
         """
         Private method to create a list of plugin archive names.
-        
+
         @return list of plugin archive names (list of strings)
         """
         archivesList = []
@@ -142,18 +151,16 @@
             self.__finishButton.setEnabled(True)
             self.__closeButton.hide()
             self.__cancelButton.show()
-            
+
             msg = self.tr(
                 "Plugin ZIP-Archives:\n{0}\n\nDestination:\n{1} ({2})"
             ).format(
                 "\n".join(self.__createArchivesList()),
                 self.destinationCombo.currentText(),
-                self.destinationCombo.itemData(
-                    self.destinationCombo.currentIndex()
-                )
+                self.destinationCombo.itemData(self.destinationCombo.currentIndex()),
             )
             self.summaryEdit.setPlainText(msg)
-    
+
     @pyqtSlot()
     def on_addArchivesButton_clicked(self):
         """
@@ -164,8 +171,9 @@
             self,
             self.tr("Select plugin ZIP-archives"),
             dn,
-            self.tr("Plugin archive (*.zip)"))
-        
+            self.tr("Plugin archive (*.zip)"),
+        )
+
         if archives:
             matchflags = Qt.MatchFlag.MatchFixedString
             if not Utilities.isWindowsPlatform():
@@ -175,34 +183,32 @@
                     # entry not in list already
                     self.archivesList.addItem(archive)
             self.archivesList.sortItems()
-        
+
         self.__nextButton.setEnabled(self.archivesList.count() > 0)
-    
+
     @pyqtSlot()
     def on_archivesList_itemSelectionChanged(self):
         """
         Private slot called, when the selection of the archives list changes.
         """
-        self.removeArchivesButton.setEnabled(
-            len(self.archivesList.selectedItems()) > 0)
-    
+        self.removeArchivesButton.setEnabled(len(self.archivesList.selectedItems()) > 0)
+
     @pyqtSlot()
     def on_removeArchivesButton_clicked(self):
         """
         Private slot to remove archives from the list.
         """
         for archiveItem in self.archivesList.selectedItems():
-            itm = self.archivesList.takeItem(
-                self.archivesList.row(archiveItem))
+            itm = self.archivesList.takeItem(self.archivesList.row(archiveItem))
             del itm
-        
+
         self.__nextButton.setEnabled(self.archivesList.count() > 0)
-    
+
     @pyqtSlot(QAbstractButton)
     def on_buttonBox_clicked(self, button):
         """
         Private slot to handle the click of a button of the button box.
-        
+
         @param button reference to the button pressed (QAbstractButton)
         """
         if button == self.__backButton:
@@ -219,18 +225,17 @@
                 self.__restartNeeded = True
             self.__closeButton.show()
             self.__cancelButton.hide()
-    
+
     def __installPlugins(self):
         """
         Private method to install the selected plugin archives.
-        
+
         @return flag indicating success (boolean)
         """
         res = True
         self.summaryEdit.clear()
         for archive in self.__createArchivesList():
-            self.summaryEdit.append(
-                self.tr("Installing {0} ...").format(archive))
+            self.summaryEdit.append(self.tr("Installing {0} ...").format(archive))
             ok, msg, restart = self.__installPlugin(archive)
             res = res and ok
             if ok:
@@ -241,18 +246,18 @@
                 self.__restartNeeded = True
         self.summaryEdit.append("\n")
         if res:
-            self.summaryEdit.append(self.tr(
-                """The plugins were installed successfully."""))
+            self.summaryEdit.append(
+                self.tr("""The plugins were installed successfully.""")
+            )
         else:
-            self.summaryEdit.append(self.tr(
-                """Some plugins could not be installed."""))
-        
+            self.summaryEdit.append(self.tr("""Some plugins could not be installed."""))
+
         return res
-    
+
     def __installPlugin(self, archiveFilename):
         """
         Private slot to install the selected plugin.
-        
+
         @param archiveFilename name of the plugin archive
             file (string)
         @return flag indicating success (boolean), error message
@@ -260,14 +265,15 @@
             of the IDE is required (boolean)
         """
         installedPluginName = ""
-        
+
         archive = archiveFilename
         destination = self.destinationCombo.itemData(
-            self.destinationCombo.currentIndex())
-        
+            self.destinationCombo.currentIndex()
+        )
+
         # check if archive is a local url
         url = urllib.parse.urlparse(archive)
-        if url[0].lower() == 'file':
+        if url[0].lower() == "file":
             archive = url[2]
 
         # check, if the archive exists
@@ -276,32 +282,35 @@
                 False,
                 self.tr(
                     """<p>The archive file <b>{0}</b> does not exist. """
-                    """Aborting...</p>""").format(archive),
-                False
+                    """Aborting...</p>"""
+                ).format(archive),
+                False,
             )
-        
+
         # check, if the archive is a valid zip file
         if not zipfile.is_zipfile(archive):
             return (
                 False,
                 self.tr(
                     """<p>The file <b>{0}</b> is not a valid plugin """
-                    """ZIP-archive. Aborting...</p>""").format(archive),
-                False
+                    """ZIP-archive. Aborting...</p>"""
+                ).format(archive),
+                False,
             )
-        
+
         # check, if the destination is writeable
         if not os.access(destination, os.W_OK):
             return (
                 False,
                 self.tr(
                     """<p>The destination directory <b>{0}</b> is not """
-                    """writeable. Aborting...</p>""").format(destination),
-                False
+                    """writeable. Aborting...</p>"""
+                ).format(destination),
+                False,
             )
-        
+
         zipFile = zipfile.ZipFile(archive, "r")
-        
+
         # check, if the archive contains a valid plugin
         pluginFound = False
         pluginFileName = ""
@@ -311,16 +320,17 @@
                 pluginFound = True
                 pluginFileName = name
                 break
-        
+
         if not pluginFound:
             return (
                 False,
                 self.tr(
                     """<p>The file <b>{0}</b> is not a valid plugin """
-                    """ZIP-archive. Aborting...</p>""").format(archive),
-                False
+                    """ZIP-archive. Aborting...</p>"""
+                ).format(archive),
+                False,
             )
-        
+
         # parse the plugin module's plugin header
         pluginSource = Utilities.decode(zipFile.read(pluginFileName))[0]
         packageName = ""
@@ -332,8 +342,8 @@
             if line.startswith("packageName"):
                 tokens = line.split("=")
                 if (
-                    tokens[0].strip() == "packageName" and
-                    tokens[1].strip()[1:-1] != "__core__"
+                    tokens[0].strip() == "packageName"
+                    and tokens[1].strip()[1:-1] != "__core__"
                 ):
                     if tokens[1].strip()[0] in ['"', "'"]:
                         packageName = tokens[1].strip()[1:-1]
@@ -358,7 +368,7 @@
                     doCompile = False
             elif line.startswith("# End-Of-Header"):
                 break
-        
+
         if not packageName:
             return (
                 False,
@@ -366,9 +376,9 @@
                     """<p>The plugin module <b>{0}</b> does not contain """
                     """a 'packageName' attribute. Aborting...</p>"""
                 ).format(pluginFileName),
-                False
+                False,
             )
-        
+
         if pyqtApi < 2:
             return (
                 False,
@@ -376,62 +386,62 @@
                     """<p>The plugin module <b>{0}</b> does not conform"""
                     """ with the PyQt v2 API. Aborting...</p>"""
                 ).format(pluginFileName),
-                False
+                False,
             )
-        
+
         # check, if it is a plugin, that collides with others
         if (
-            not os.path.exists(os.path.join(destination, pluginFileName)) and
-            packageName != "None" and
-            os.path.exists(os.path.join(destination, packageName))
+            not os.path.exists(os.path.join(destination, pluginFileName))
+            and packageName != "None"
+            and os.path.exists(os.path.join(destination, packageName))
         ):
             return (
                 False,
-                self.tr("""<p>The plugin package <b>{0}</b> exists. """
-                        """Aborting...</p>""").format(
-                    os.path.join(destination, packageName)),
-                False
+                self.tr(
+                    """<p>The plugin package <b>{0}</b> exists. """
+                    """Aborting...</p>"""
+                ).format(os.path.join(destination, packageName)),
+                False,
             )
-        
+
         if (
-            os.path.exists(os.path.join(destination, pluginFileName)) and
-            packageName != "None" and
-            not os.path.exists(os.path.join(destination, packageName))
+            os.path.exists(os.path.join(destination, pluginFileName))
+            and packageName != "None"
+            and not os.path.exists(os.path.join(destination, packageName))
         ):
             return (
                 False,
-                self.tr("""<p>The plugin module <b>{0}</b> exists. """
-                        """Aborting...</p>""").format(
-                    os.path.join(destination, pluginFileName)),
-                False
+                self.tr(
+                    """<p>The plugin module <b>{0}</b> exists. """ """Aborting...</p>"""
+                ).format(os.path.join(destination, pluginFileName)),
+                False,
             )
-        
+
         activatePlugin = False
         if not self.__external:
-            activatePlugin = (
-                not self.__pluginManager.isPluginLoaded(
-                    installedPluginName) or
-                (self.__pluginManager.isPluginLoaded(installedPluginName) and
-                 self.__pluginManager.isPluginActive(installedPluginName))
+            activatePlugin = not self.__pluginManager.isPluginLoaded(
+                installedPluginName
+            ) or (
+                self.__pluginManager.isPluginLoaded(installedPluginName)
+                and self.__pluginManager.isPluginActive(installedPluginName)
             )
             # try to unload a plugin with the same name
             self.__pluginManager.unloadPlugin(installedPluginName)
-        
+
         # uninstall existing plug-in first to get clean conditions
-        if (
-            packageName != "None" and
-            not os.path.exists(
-                os.path.join(destination, packageName, "__init__.py"))
+        if packageName != "None" and not os.path.exists(
+            os.path.join(destination, packageName, "__init__.py")
         ):
             # package directory contains just data, don't delete it
             self.__uninstallPackage(destination, pluginFileName, "")
         else:
             self.__uninstallPackage(destination, pluginFileName, packageName)
-        
+
         # clean sys.modules
         reload_ = self.__pluginManager.removePluginFromSysModules(
-            installedPluginName, packageName, internalPackages)
-        
+            installedPluginName, packageName, internalPackages
+        )
+
         # now do the installation
         self.__installedDirs = []
         self.__installedFiles = []
@@ -441,7 +451,7 @@
                 tot = len(namelist)
                 self.progress.setMaximum(tot)
                 QApplication.processEvents()
-                
+
                 now = time.monotonic()
                 for prog, name in enumerate(namelist):
                     self.progress.setValue(prog)
@@ -449,9 +459,9 @@
                         QApplication.processEvents()
                         now = time.monotonic()
                     if (
-                        name == pluginFileName or
-                        name.startswith("{0}/".format(packageName)) or
-                        name.startswith("{0}\\".format(packageName))
+                        name == pluginFileName
+                        or name.startswith("{0}/".format(packageName))
+                        or name.startswith("{0}\\".format(packageName))
                     ):
                         outname = name.replace("/", os.sep)
                         outname = os.path.join(destination, outname)
@@ -480,19 +490,14 @@
             self.__rollback()
             return (
                 False,
-                self.tr("Error installing plugin. Reason: {0}")
-                .format(str(why)),
-                False
+                self.tr("Error installing plugin. Reason: {0}").format(str(why)),
+                False,
             )
         except Exception:
             sys.stderr.write("Unspecific exception installing plugin.\n")
             self.__rollback()
-            return (
-                False,
-                self.tr("Unspecific exception installing plugin."),
-                False
-            )
-        
+            return (False, self.tr("Unspecific exception installing plugin."), False)
+
         # now compile the plugins
         if doCompile:
             dirName = os.path.join(destination, packageName)
@@ -501,15 +506,16 @@
             compileall.compile_dir(dirName, quiet=True)
             compileall.compile_file(files, quiet=True)
             os.path.join_unicode = True
-        
+
             # now load and activate the plugin
         self.__pluginManager.loadPlugin(
-            installedPluginName, destination, reload_=reload_, install=True)
+            installedPluginName, destination, reload_=reload_, install=True
+        )
         if activatePlugin and not self.__external:
             self.__pluginManager.activatePlugin(installedPluginName)
-        
+
         return True, "", needsRestart
-    
+
     def __rollback(self):
         """
         Private method to rollback a failed installation.
@@ -520,14 +526,14 @@
         for dname in self.__installedDirs:
             if os.path.exists(dname):
                 shutil.rmtree(dname)
-    
+
     def __makedirs(self, name, mode=0o777):
         """
         Private method to create a directory and all intermediate ones.
-        
+
         This is an extended version of the Python one in order to
         record the created directories.
-        
+
         @param name name of the directory to create (string)
         @param mode permission to set for the new directory (integer)
         """
@@ -541,49 +547,47 @@
                 return
         os.mkdir(name, mode)
         self.__installedDirs.append(name)
-    
+
     def __uninstallPackage(self, destination, pluginFileName, packageName):
         """
         Private method to uninstall an already installed plugin to prepare
         the update.
-        
+
         @param destination name of the plugin directory (string)
         @param pluginFileName name of the plugin file (string)
         @param packageName name of the plugin package (string)
         """
         packageDir = (
             None
-            if packageName in ("", "None") else
-            os.path.join(destination, packageName)
+            if packageName in ("", "None")
+            else os.path.join(destination, packageName)
         )
         pluginFile = os.path.join(destination, pluginFileName)
-        
+
         with contextlib.suppress(OSError, os.error):
             if packageDir and os.path.exists(packageDir):
                 shutil.rmtree(packageDir)
-            
+
             fnameo = "{0}o".format(pluginFile)
             if os.path.exists(fnameo):
                 os.remove(fnameo)
-            
+
             fnamec = "{0}c".format(pluginFile)
             if os.path.exists(fnamec):
                 os.remove(fnamec)
-            
-            pluginDirCache = os.path.join(
-                os.path.dirname(pluginFile), "__pycache__")
+
+            pluginDirCache = os.path.join(os.path.dirname(pluginFile), "__pycache__")
             if os.path.exists(pluginDirCache):
-                pluginFileName = os.path.splitext(
-                    os.path.basename(pluginFile))[0]
+                pluginFileName = os.path.splitext(os.path.basename(pluginFile))[0]
                 for fnameo in glob.glob(
-                    os.path.join(pluginDirCache,
-                                 "{0}*.pyo".format(pluginFileName))):
+                    os.path.join(pluginDirCache, "{0}*.pyo".format(pluginFileName))
+                ):
                     os.remove(fnameo)
                 for fnamec in glob.glob(
-                    os.path.join(pluginDirCache,
-                                 "{0}*.pyc".format(pluginFileName))):
+                    os.path.join(pluginDirCache, "{0}*.pyc".format(pluginFileName))
+                ):
                     os.remove(fnamec)
-            
+
             os.remove(pluginFile)
 
 
@@ -591,10 +595,11 @@
     """
     Class for the dialog variant.
     """
+
     def __init__(self, pluginManager, pluginFileNames, parent=None):
         """
         Constructor
-        
+
         @param pluginManager reference to the plugin manager object
         @param pluginFileNames list of plugin files suggested for
             installation (list of strings)
@@ -602,24 +607,24 @@
         """
         super().__init__(parent)
         self.setSizeGripEnabled(True)
-        
+
         self.__layout = QVBoxLayout(self)
         self.__layout.setContentsMargins(0, 0, 0, 0)
         self.setLayout(self.__layout)
-        
+
         self.cw = PluginInstallWidget(pluginManager, pluginFileNames, self)
         size = self.cw.size()
         self.__layout.addWidget(self.cw)
         self.resize(size)
         self.setWindowTitle(self.cw.windowTitle())
-        
+
         self.cw.buttonBox.accepted.connect(self.accept)
         self.cw.buttonBox.rejected.connect(self.reject)
-    
+
     def restartNeeded(self):
         """
         Public method to check, if a restart of the IDE is required.
-        
+
         @return flag indicating a restart is required (boolean)
         """
         return self.cw.restartNeeded()
@@ -629,10 +634,11 @@
     """
     Main window class for the standalone dialog.
     """
+
     def __init__(self, pluginFileNames, parent=None):
         """
         Constructor
-        
+
         @param pluginFileNames list of plugin files suggested for
             installation (list of strings)
         @param parent reference to the parent widget (QWidget)
@@ -643,9 +649,8 @@
         self.setCentralWidget(self.cw)
         self.resize(size)
         self.setWindowTitle(self.cw.windowTitle())
-        
-        self.setStyle(Preferences.getUI("Style"),
-                      Preferences.getUI("StyleSheet"))
-        
+
+        self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet"))
+
         self.cw.buttonBox.accepted.connect(self.close)
         self.cw.buttonBox.rejected.connect(self.close)

eric ide

mercurial