src/eric7/PipInterface/PipPackagesWidget.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9234
97bdad5be46d
diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/PipInterface/PipPackagesWidget.py
--- a/src/eric7/PipInterface/PipPackagesWidget.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/PipInterface/PipPackagesWidget.py	Wed Jul 13 14:55:47 2022 +0200
@@ -18,8 +18,14 @@
 from PyQt6.QtGui import QIcon
 from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest
 from PyQt6.QtWidgets import (
-    QWidget, QToolButton, QApplication, QHeaderView, QTreeWidgetItem,
-    QMenu, QDialog, QAbstractItemView
+    QWidget,
+    QToolButton,
+    QApplication,
+    QHeaderView,
+    QTreeWidgetItem,
+    QMenu,
+    QDialog,
+    QAbstractItemView,
 )
 
 from EricWidgets.EricApplication import ericApp
@@ -38,12 +44,13 @@
     """
     Class implementing the parser for the PyPI search result page.
     """
+
     ClassPrefix = "package-snippet__"
-    
+
     def __init__(self, data):
         """
         Constructor
-        
+
         @param data data to be parsed
         @type str
         """
@@ -51,12 +58,12 @@
         self.__results = []
         self.__activeClass = None
         self.feed(data)
-    
+
     def __getClass(self, attrs):
         """
         Private method to extract the class attribute out of the list of
         attributes.
-        
+
         @param attrs list of tag attributes as (name, value) tuples
         @type list of tuple of (str, str)
         @return value of the 'class' attribute or None
@@ -65,14 +72,14 @@
         for name, value in attrs:
             if name == "class":
                 return value
-        
+
         return None
-    
+
     def __getDate(self, attrs):
         """
         Private method to extract the datetime attribute out of the list of
         attributes and process it.
-        
+
         @param attrs list of tag attributes as (name, value) tuples
         @type list of tuple of (str, str)
         @return value of the 'class' attribute or None
@@ -81,13 +88,13 @@
         for name, value in attrs:
             if name == "datetime":
                 return value.split("T")[0]
-        
+
         return None
-    
+
     def handle_starttag(self, tag, attrs):
         """
         Public method to process the start tag.
-        
+
         @param tag tag name (all lowercase)
         @type str
         @param attrs list of tag attributes as (name, value) tuples
@@ -95,12 +102,14 @@
         """
         if tag == "a" and self.__getClass(attrs) == "package-snippet":
             self.__results.append({})
-        
+
         if tag in ("span", "p"):
             tagClass = self.__getClass(attrs)
             if tagClass in (
-                "package-snippet__name", "package-snippet__description",
-                "package-snippet__version", "package-snippet__released",
+                "package-snippet__name",
+                "package-snippet__description",
+                "package-snippet__version",
+                "package-snippet__released",
                 "package-snippet__created",
             ):
                 self.__activeClass = tagClass
@@ -112,31 +121,31 @@
             self.__activeClass = None
         else:
             self.__activeClass = None
-    
+
     def handle_data(self, data):
         """
         Public method process arbitrary data.
-        
+
         @param data data to be processed
         @type str
         """
         if self.__activeClass is not None:
             attributeName = self.__activeClass.replace(self.ClassPrefix, "")
             self.__results[-1][attributeName] = data
-    
+
     def handle_endtag(self, tag):
         """
         Public method to process the end tag.
-        
+
         @param tag tag name (all lowercase)
         @type str
         """
         self.__activeClass = None
-    
+
     def getResults(self):
         """
         Public method to get the extracted search results.
-        
+
         @return extracted result data
         @rtype list of dict
         """
@@ -147,27 +156,28 @@
     """
     Class implementing the pip packages management widget.
     """
+
     ShowProcessGeneralMode = 0
     ShowProcessClassifiersMode = 1
     ShowProcessEntryPointsMode = 2
     ShowProcessFilesListMode = 3
-    
+
     SearchVersionRole = Qt.ItemDataRole.UserRole + 1
     VulnerabilityRole = Qt.ItemDataRole.UserRole + 2
-    
+
     PackageColumn = 0
     InstalledVersionColumn = 1
     AvailableVersionColumn = 2
     VulnerabilityColumn = 3
-    
+
     DepPackageColumn = 0
     DepInstalledVersionColumn = 1
     DepRequiredVersionColumn = 2
-    
+
     def __init__(self, pip, parent=None):
         """
         Constructor
-        
+
         @param pip reference to the global pip interface
         @type Pip
         @param parent reference to the parent widget
@@ -175,58 +185,44 @@
         """
         super().__init__(parent)
         self.setupUi(self)
-        
+
         self.layout().setContentsMargins(0, 3, 0, 0)
-        
+
         self.viewToggleButton.setIcon(UI.PixmapCache.getIcon("viewListTree"))
-        
-        self.pipMenuButton.setObjectName(
-            "pip_supermenu_button")
+
+        self.pipMenuButton.setObjectName("pip_supermenu_button")
         self.pipMenuButton.setIcon(UI.PixmapCache.getIcon("superMenu"))
         self.pipMenuButton.setToolTip(self.tr("pip Menu"))
-        self.pipMenuButton.setPopupMode(
-            QToolButton.ToolButtonPopupMode.InstantPopup)
-        self.pipMenuButton.setToolButtonStyle(
-            Qt.ToolButtonStyle.ToolButtonIconOnly)
+        self.pipMenuButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
+        self.pipMenuButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
         self.pipMenuButton.setFocusPolicy(Qt.FocusPolicy.NoFocus)
         self.pipMenuButton.setAutoRaise(True)
         self.pipMenuButton.setShowMenuInside(True)
-        
-        self.refreshButton.setIcon(
-            UI.PixmapCache.getIcon("reload"))
-        self.upgradeButton.setIcon(
-            UI.PixmapCache.getIcon("1uparrow"))
-        self.upgradeAllButton.setIcon(
-            UI.PixmapCache.getIcon("2uparrow"))
-        self.uninstallButton.setIcon(
-            UI.PixmapCache.getIcon("minus"))
-        self.showPackageDetailsButton.setIcon(
-            UI.PixmapCache.getIcon("info"))
-        self.searchToggleButton.setIcon(
-            UI.PixmapCache.getIcon("find"))
-        self.searchButton.setIcon(
-            UI.PixmapCache.getIcon("findNext"))
-        self.searchMoreButton.setIcon(
-            UI.PixmapCache.getIcon("plus"))
-        self.installButton.setIcon(
-            UI.PixmapCache.getIcon("plus"))
-        self.installUserSiteButton.setIcon(
-            UI.PixmapCache.getIcon("addUser"))
-        self.showDetailsButton.setIcon(
-            UI.PixmapCache.getIcon("info"))
-        
-        self.refreshDependenciesButton.setIcon(
-            UI.PixmapCache.getIcon("reload"))
-        self.showDepPackageDetailsButton.setIcon(
-            UI.PixmapCache.getIcon("info"))
-        
+
+        self.refreshButton.setIcon(UI.PixmapCache.getIcon("reload"))
+        self.upgradeButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
+        self.upgradeAllButton.setIcon(UI.PixmapCache.getIcon("2uparrow"))
+        self.uninstallButton.setIcon(UI.PixmapCache.getIcon("minus"))
+        self.showPackageDetailsButton.setIcon(UI.PixmapCache.getIcon("info"))
+        self.searchToggleButton.setIcon(UI.PixmapCache.getIcon("find"))
+        self.searchButton.setIcon(UI.PixmapCache.getIcon("findNext"))
+        self.searchMoreButton.setIcon(UI.PixmapCache.getIcon("plus"))
+        self.installButton.setIcon(UI.PixmapCache.getIcon("plus"))
+        self.installUserSiteButton.setIcon(UI.PixmapCache.getIcon("addUser"))
+        self.showDetailsButton.setIcon(UI.PixmapCache.getIcon("info"))
+
+        self.refreshDependenciesButton.setIcon(UI.PixmapCache.getIcon("reload"))
+        self.showDepPackageDetailsButton.setIcon(UI.PixmapCache.getIcon("info"))
+
         self.__pip = pip
-        
+
         self.packagesList.header().setSortIndicator(
-            PipPackagesWidget.PackageColumn, Qt.SortOrder.AscendingOrder)
+            PipPackagesWidget.PackageColumn, Qt.SortOrder.AscendingOrder
+        )
         self.dependenciesList.header().setSortIndicator(
-            PipPackagesWidget.DepPackageColumn, Qt.SortOrder.AscendingOrder)
-        
+            PipPackagesWidget.DepPackageColumn, Qt.SortOrder.AscendingOrder
+        )
+
         self.__infoLabels = {
             "name": self.tr("Name:"),
             "version": self.tr("Version:"),
@@ -245,38 +241,34 @@
         }
         self.infoWidget.setHeaderLabels(["Key", "Value"])
         self.dependencyInfoWidget.setHeaderLabels(["Key", "Value"])
-        
+
         venvManager = ericApp().getObject("VirtualEnvManager")
-        venvManager.virtualEnvironmentAdded.connect(
-            self.on_refreshButton_clicked)
-        venvManager.virtualEnvironmentRemoved.connect(
-            self.on_refreshButton_clicked)
+        venvManager.virtualEnvironmentAdded.connect(self.on_refreshButton_clicked)
+        venvManager.virtualEnvironmentRemoved.connect(self.on_refreshButton_clicked)
         self.__selectedEnvironment = None
-        
+
         project = ericApp().getObject("Project")
-        project.projectOpened.connect(
-            self.__projectOpened)
-        project.projectClosed.connect(
-            self.__projectClosed)
-        
+        project.projectOpened.connect(self.__projectOpened)
+        project.projectClosed.connect(self.__projectClosed)
+
         self.__initPipMenu()
         self.__populateEnvironments()
         self.__updateActionButtons()
         self.__updateDepActionButtons()
-        
+
         self.statusLabel.hide()
         self.searchWidget.hide()
         self.__lastSearchPage = 0
-        
+
         self.__queryName = []
         self.__querySummary = []
-        
+
         self.__replies = []
-        
+
         self.__packageDetailsDialog = None
-        
+
         self.viewsStackWidget.setCurrentWidget(self.packagesPage)
-    
+
     @pyqtSlot()
     def __projectOpened(self):
         """
@@ -285,19 +277,19 @@
         projectVenv = self.__pip.getProjectEnvironmentString()
         if projectVenv:
             self.environmentsComboBox.insertItem(1, projectVenv)
-    
+
     @pyqtSlot(bool)
     def __projectClosed(self, shutdown):
         """
         Private slot to handle the projectClosed signal.
-        
+
         @param shutdown flag indicating the IDE shutdown
         @type bool
         """
         if not shutdown:
             # the project entry is always at index 1
             self.environmentsComboBox.removeItem(1)
-    
+
     def __populateEnvironments(self):
         """
         Private method to get a list of environments and populate the selector.
@@ -308,36 +300,38 @@
             self.environmentsComboBox.addItem(projectVenv)
         self.environmentsComboBox.addItems(
             self.__pip.getVirtualenvNames(
-                noRemote=True,
-                noConda=Preferences.getPip("ExcludeCondaEnvironments")
+                noRemote=True, noConda=Preferences.getPip("ExcludeCondaEnvironments")
             )
         )
-    
+
     def __isPipAvailable(self):
         """
         Private method to check, if the pip package is available for the
         selected environment.
-        
+
         @return flag indicating availability
         @rtype bool
         """
         available = False
-        
+
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             available = (
-                len(self.packagesList.findItems(
-                    "pip",
-                    Qt.MatchFlag.MatchExactly |
-                    Qt.MatchFlag.MatchCaseSensitive)) == 1
+                len(
+                    self.packagesList.findItems(
+                        "pip",
+                        Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive,
+                    )
+                )
+                == 1
             )
-        
+
         return available
-    
+
     def __availablePipVersion(self):
         """
         Private method to get the pip version of the selected environment.
-        
+
         @return tuple containing the version number or tuple with all zeros
             in case pip is not available
         @rtype tuple of int
@@ -346,44 +340,45 @@
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             pipList = self.packagesList.findItems(
-                "pip",
-                Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive
+                "pip", Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive
             )
             if len(pipList) > 0:
                 pipVersionTuple = Globals.versionToTuple(
-                    pipList[0].text(PipPackagesWidget.InstalledVersionColumn))
-        
+                    pipList[0].text(PipPackagesWidget.InstalledVersionColumn)
+                )
+
         return pipVersionTuple
-    
+
     def getPip(self):
         """
         Public method to get a reference to the pip interface object.
-        
+
         @return reference to the pip interface object
         @rtype Pip
         """
         return self.__pip
-    
+
     #######################################################################
     ## Slots handling widget signals below
     #######################################################################
-    
+
     def __selectedUpdateableItems(self):
         """
         Private method to get a list of selected items that can be updated.
-        
+
         @return list of selected items that can be updated
         @rtype list of QTreeWidgetItem
         """
         return [
-            itm for itm in self.packagesList.selectedItems()
+            itm
+            for itm in self.packagesList.selectedItems()
             if bool(itm.text(PipPackagesWidget.AvailableVersionColumn))
         ]
-    
+
     def __allUpdateableItems(self):
         """
         Private method to get a list of all items that can be updated.
-        
+
         @return list of all items that can be updated
         @rtype list of QTreeWidgetItem
         """
@@ -392,28 +387,26 @@
             itm = self.packagesList.topLevelItem(index)
             if itm.text(PipPackagesWidget.AvailableVersionColumn):
                 updateableItems.append(itm)
-        
+
         return updateableItems
-    
+
     def __updateActionButtons(self):
         """
         Private method to set the state of the action buttons.
         """
         if self.__isPipAvailable():
-            self.upgradeButton.setEnabled(
-                bool(self.__selectedUpdateableItems()))
-            self.uninstallButton.setEnabled(
-                bool(self.packagesList.selectedItems()))
-            self.upgradeAllButton.setEnabled(
-                bool(self.__allUpdateableItems()))
+            self.upgradeButton.setEnabled(bool(self.__selectedUpdateableItems()))
+            self.uninstallButton.setEnabled(bool(self.packagesList.selectedItems()))
+            self.upgradeAllButton.setEnabled(bool(self.__allUpdateableItems()))
             self.showPackageDetailsButton.setEnabled(
-                len(self.packagesList.selectedItems()) == 1)
+                len(self.packagesList.selectedItems()) == 1
+            )
         else:
             self.upgradeButton.setEnabled(False)
             self.uninstallButton.setEnabled(False)
             self.upgradeAllButton.setEnabled(False)
             self.showPackageDetailsButton.setEnabled(False)
-    
+
     def __refreshPackagesList(self):
         """
         Private method to refresh the packages list.
@@ -424,9 +417,8 @@
             interpreter = self.__pip.getVirtualenvInterpreter(venvName)
             if interpreter:
                 self.statusLabel.show()
-                self.statusLabel.setText(
-                    self.tr("Getting installed packages..."))
-                
+                self.statusLabel.setText(self.tr("Getting installed packages..."))
+
                 with EricOverrideCursor():
                     # 1. populate with installed packages
                     self.packagesList.setUpdatesEnabled(False)
@@ -437,13 +429,11 @@
                         usersite=self.userCheckBox.isChecked(),
                     )
                     for package, version in installedPackages:
-                        QTreeWidgetItem(self.packagesList,
-                                        [package, version, "", ""])
+                        QTreeWidgetItem(self.packagesList, [package, version, "", ""])
                     self.packagesList.setUpdatesEnabled(True)
-                    self.statusLabel.setText(
-                        self.tr("Getting outdated packages..."))
+                    self.statusLabel.setText(self.tr("Getting outdated packages..."))
                     QApplication.processEvents()
-                    
+
                     # 2. update with update information
                     self.packagesList.setUpdatesEnabled(False)
                     outdatedPackages = self.__pip.getOutdatedPackages(
@@ -455,37 +445,36 @@
                     for package, _version, latest in outdatedPackages:
                         items = self.packagesList.findItems(
                             package,
-                            Qt.MatchFlag.MatchExactly |
-                            Qt.MatchFlag.MatchCaseSensitive
+                            Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive,
                         )
                         if items:
                             itm = items[0]
                             itm.setText(
-                                PipPackagesWidget.AvailableVersionColumn,
-                                latest)
-                    
+                                PipPackagesWidget.AvailableVersionColumn, latest
+                            )
+
                     self.packagesList.sortItems(
-                        PipPackagesWidget.PackageColumn,
-                        Qt.SortOrder.AscendingOrder)
+                        PipPackagesWidget.PackageColumn, Qt.SortOrder.AscendingOrder
+                    )
                     for col in range(self.packagesList.columnCount()):
                         self.packagesList.resizeColumnToContents(col)
                     self.packagesList.setUpdatesEnabled(True)
-                    
+
                     # 3. update with vulnerability information
                     if self.vulnerabilityCheckBox.isChecked():
                         self.__updateVulnerabilityData()
                 self.statusLabel.hide()
-        
+
         self.__updateActionButtons()
         self.__updateSearchActionButtons()
         self.__updateSearchButton()
         self.__updateSearchMoreButton(False)
-    
+
     @pyqtSlot(str)
     def on_environmentsComboBox_currentTextChanged(self, name):
         """
         Private slot handling the selection of a Python environment.
-        
+
         @param name name of the selected Python environment
         @type str
         """
@@ -495,32 +484,32 @@
             else:
                 self.__refreshPackagesList()
             self.__selectedEnvironment = name
-    
+
     @pyqtSlot()
     def on_localCheckBox_clicked(self):
         """
         Private slot handling the switching of the local mode.
         """
         self.__refreshPackagesList()
-    
+
     @pyqtSlot()
     def on_notRequiredCheckBox_clicked(self):
         """
         Private slot handling the switching of the 'not required' mode.
         """
         self.__refreshPackagesList()
-    
+
     @pyqtSlot()
     def on_userCheckBox_clicked(self):
         """
         Private slot handling the switching of the 'user-site' mode.
         """
         self.__refreshPackagesList()
-    
+
     def __showPackageInformation(self, packageName, infoWidget):
         """
         Private method to show information for a package.
-        
+
         @param packageName name of the package
         @type str
         @param infoWidget reference to the widget to contain the information
@@ -530,17 +519,17 @@
         interpreter = self.__pip.getVirtualenvInterpreter(environment)
         if not interpreter:
             return
-        
+
         args = ["-m", "pip", "show"]
         if self.verboseCheckBox.isChecked():
             args.append("--verbose")
         if self.installedFilesCheckBox.isChecked():
             args.append("--files")
         args.append(packageName)
-        
+
         with EricOverrideCursor():
             success, output = self.__pip.runProcess(args, interpreter)
-            
+
             if success and output:
                 mode = self.ShowProcessGeneralMode
                 for line in output.splitlines():
@@ -548,9 +537,7 @@
                     if line != "---":
                         if mode != self.ShowProcessGeneralMode:
                             if line[0] == " ":
-                                QTreeWidgetItem(
-                                    infoWidget,
-                                    [" ", line.strip()])
+                                QTreeWidgetItem(infoWidget, [" ", line.strip()])
                             else:
                                 mode = self.ShowProcessGeneralMode
                         if mode == self.ShowProcessGeneralMode:
@@ -562,8 +549,8 @@
                             label = label.lower()
                             if label in self.__infoLabels:
                                 QTreeWidgetItem(
-                                    infoWidget,
-                                    [self.__infoLabels[label], info])
+                                    infoWidget, [self.__infoLabels[label], info]
+                                )
                             if label == "files":
                                 mode = self.ShowProcessFilesListMode
                             elif label == "classifiers":
@@ -571,16 +558,13 @@
                             elif label == "entry-points":
                                 mode = self.ShowProcessEntryPointsMode
                 infoWidget.scrollToTop()
-            
+
             header = infoWidget.header()
             header.setStretchLastSection(False)
             header.resizeSections(QHeaderView.ResizeMode.ResizeToContents)
-            if (
-                header.sectionSize(0) + header.sectionSize(1) <
-                header.width()
-            ):
+            if header.sectionSize(0) + header.sectionSize(1) < header.width():
                 header.setStretchLastSection(True)
-    
+
     @pyqtSlot()
     def on_packagesList_itemSelectionChanged(self):
         """
@@ -588,43 +572,43 @@
         """
         if len(self.packagesList.selectedItems()) == 0:
             self.infoWidget.clear()
-    
+
     @pyqtSlot(QTreeWidgetItem, int)
     def on_packagesList_itemPressed(self, item, column):
         """
         Private slot reacting on a package item being pressed.
-        
+
         @param item reference to the pressed item
         @type QTreeWidgetItem
         @param column pressed column
         @type int
         """
         self.infoWidget.clear()
-        
+
         if item is not None:
-            if (
-                column == PipPackagesWidget.VulnerabilityColumn and
-                bool(item.text(PipPackagesWidget.VulnerabilityColumn))
+            if column == PipPackagesWidget.VulnerabilityColumn and bool(
+                item.text(PipPackagesWidget.VulnerabilityColumn)
             ):
                 self.__showVulnerabilityInformation(
                     item.text(PipPackagesWidget.PackageColumn),
                     item.text(PipPackagesWidget.InstalledVersionColumn),
-                    item.data(PipPackagesWidget.VulnerabilityColumn,
-                              PipPackagesWidget.VulnerabilityRole)
+                    item.data(
+                        PipPackagesWidget.VulnerabilityColumn,
+                        PipPackagesWidget.VulnerabilityRole,
+                    ),
                 )
             else:
                 self.__showPackageInformation(
-                    item.text(PipPackagesWidget.PackageColumn),
-                    self.infoWidget
+                    item.text(PipPackagesWidget.PackageColumn), self.infoWidget
                 )
-        
+
         self.__updateActionButtons()
-    
+
     @pyqtSlot(QTreeWidgetItem, int)
     def on_packagesList_itemActivated(self, item, column):
         """
         Private slot reacting on a package item being activated.
-        
+
         @param item reference to the activated item
         @type QTreeWidgetItem
         @param column activated column
@@ -634,44 +618,42 @@
         upgradable = bool(item.text(PipPackagesWidget.AvailableVersionColumn))
         if column == PipPackagesWidget.InstalledVersionColumn:
             # show details for installed version
-            packageVersion = item.text(
-                PipPackagesWidget.InstalledVersionColumn)
+            packageVersion = item.text(PipPackagesWidget.InstalledVersionColumn)
         else:
             # show details for available version or installed one
             if item.text(PipPackagesWidget.AvailableVersionColumn):
-                packageVersion = item.text(
-                    PipPackagesWidget.AvailableVersionColumn)
+                packageVersion = item.text(PipPackagesWidget.AvailableVersionColumn)
             else:
-                packageVersion = item.text(
-                    PipPackagesWidget.InstalledVersionColumn)
-        
-        self.__showPackageDetails(packageName, packageVersion,
-                                  upgradable=upgradable)
-    
+                packageVersion = item.text(PipPackagesWidget.InstalledVersionColumn)
+
+        self.__showPackageDetails(packageName, packageVersion, upgradable=upgradable)
+
     @pyqtSlot(bool)
     def on_verboseCheckBox_clicked(self, checked):
         """
         Private slot to handle a change of the verbose package information
         checkbox.
-        
+
         @param checked state of the checkbox
         @type bool
         """
-        self.on_packagesList_itemPressed(self.packagesList.currentItem(),
-                                         self.packagesList.currentColumn())
-    
+        self.on_packagesList_itemPressed(
+            self.packagesList.currentItem(), self.packagesList.currentColumn()
+        )
+
     @pyqtSlot(bool)
     def on_installedFilesCheckBox_clicked(self, checked):
         """
         Private slot to handle a change of the installed files information
         checkbox.
-        
+
         @param checked state of the checkbox
         @type bool
         """
-        self.on_packagesList_itemPressed(self.packagesList.currentItem(),
-                                         self.packagesList.currentColumn())
-    
+        self.on_packagesList_itemPressed(
+            self.packagesList.currentItem(), self.packagesList.currentColumn()
+        )
+
     @pyqtSlot()
     def on_refreshButton_clicked(self):
         """
@@ -680,75 +662,83 @@
         currentEnvironment = self.environmentsComboBox.currentText()
         self.environmentsComboBox.clear()
         self.packagesList.clear()
-        
+
         with EricOverrideCursor():
             self.__populateEnvironments()
-            
+
             index = self.environmentsComboBox.findText(
                 currentEnvironment,
-                Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive
+                Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive,
             )
             if index != -1:
                 self.environmentsComboBox.setCurrentIndex(index)
-        
+
         self.__updateActionButtons()
-    
+
     @pyqtSlot()
     def on_upgradeButton_clicked(self):
         """
         Private slot to upgrade selected packages of the selected environment.
         """
-        packages = [itm.text(PipPackagesWidget.PackageColumn)
-                    for itm in self.__selectedUpdateableItems()]
+        packages = [
+            itm.text(PipPackagesWidget.PackageColumn)
+            for itm in self.__selectedUpdateableItems()
+        ]
         if packages:
             self.executeUpgradePackages(packages)
-    
+
     @pyqtSlot()
     def on_upgradeAllButton_clicked(self):
         """
         Private slot to upgrade all packages of the selected environment.
         """
-        packages = [itm.text(PipPackagesWidget.PackageColumn)
-                    for itm in self.__allUpdateableItems()]
+        packages = [
+            itm.text(PipPackagesWidget.PackageColumn)
+            for itm in self.__allUpdateableItems()
+        ]
         if packages:
             self.executeUpgradePackages(packages)
-    
+
     @pyqtSlot()
     def on_uninstallButton_clicked(self):
         """
         Private slot to remove selected packages of the selected environment.
         """
-        packages = [itm.text(PipPackagesWidget.PackageColumn)
-                    for itm in self.packagesList.selectedItems()]
+        packages = [
+            itm.text(PipPackagesWidget.PackageColumn)
+            for itm in self.packagesList.selectedItems()
+        ]
         self.executeUninstallPackages(packages)
-    
+
     def executeUninstallPackages(self, packages):
         """
         Public method to uninstall the given list of packages.
-        
+
         @param packages list of package names to be uninstalled
         @type list of str
         """
         if packages:
             ok = self.__pip.uninstallPackages(
-                packages,
-                venvName=self.environmentsComboBox.currentText())
+                packages, venvName=self.environmentsComboBox.currentText()
+            )
             if ok:
                 self.on_refreshButton_clicked()
-    
+
     def executeUpgradePackages(self, packages):
         """
         Public method to execute the pip upgrade command.
-        
+
         @param packages list of package names to be upgraded
         @type list of str
         """
         ok = self.__pip.upgradePackages(
-            packages, venvName=self.environmentsComboBox.currentText(),
-            userSite=self.userCheckBox.isChecked())
+            packages,
+            venvName=self.environmentsComboBox.currentText(),
+            userSite=self.userCheckBox.isChecked(),
+        )
         if ok:
             self.on_refreshButton_clicked()
-    
+
     @pyqtSlot()
     def on_showPackageDetailsButton_clicked(self):
         """
@@ -757,144 +747,135 @@
         item = self.packagesList.selectedItems()[0]
         if item:
             packageName = item.text(PipPackagesWidget.PackageColumn)
-            upgradable = bool(item.text(
-                PipPackagesWidget.AvailableVersionColumn))
+            upgradable = bool(item.text(PipPackagesWidget.AvailableVersionColumn))
             # show details for available version or installed one
             if item.text(PipPackagesWidget.AvailableVersionColumn):
-                packageVersion = item.text(
-                    PipPackagesWidget.AvailableVersionColumn)
+                packageVersion = item.text(PipPackagesWidget.AvailableVersionColumn)
             else:
-                packageVersion = item.text(
-                    PipPackagesWidget.InstalledVersionColumn)
-            
-            self.__showPackageDetails(packageName, packageVersion,
-                                      upgradable=upgradable)
-    
+                packageVersion = item.text(PipPackagesWidget.InstalledVersionColumn)
+
+            self.__showPackageDetails(
+                packageName, packageVersion, upgradable=upgradable
+            )
+
     #######################################################################
     ## Search widget related methods below
     #######################################################################
-    
+
     def __updateSearchActionButtons(self):
         """
         Private method to update the action button states of the search widget.
         """
         installEnable = (
-            len(self.searchResultList.selectedItems()) > 0 and
-            self.environmentsComboBox.currentIndex() > 0 and
-            self.__isPipAvailable()
+            len(self.searchResultList.selectedItems()) > 0
+            and self.environmentsComboBox.currentIndex() > 0
+            and self.__isPipAvailable()
         )
         self.installButton.setEnabled(installEnable)
         self.installUserSiteButton.setEnabled(installEnable)
-        
+
         self.showDetailsButton.setEnabled(
-            len(self.searchResultList.selectedItems()) == 1 and
-            self.__isPipAvailable()
+            len(self.searchResultList.selectedItems()) == 1 and self.__isPipAvailable()
         )
-    
+
     def __updateSearchButton(self):
         """
         Private method to update the state of the search button.
         """
         self.searchButton.setEnabled(
-            bool(self.searchEditName.text()) and
-            self.__isPipAvailable()
+            bool(self.searchEditName.text()) and self.__isPipAvailable()
         )
-    
+
     def __updateSearchMoreButton(self, enable):
         """
         Private method to update the state of the search more button.
-        
+
         @param enable flag indicating the desired enable state
         @type bool
         """
         self.searchMoreButton.setEnabled(
-            enable and
-            bool(self.searchEditName.text()) and
-            self.__isPipAvailable()
+            enable and bool(self.searchEditName.text()) and self.__isPipAvailable()
         )
-    
+
     @pyqtSlot(bool)
     def on_searchToggleButton_toggled(self, checked):
         """
         Private slot to togle the search widget.
-        
+
         @param checked state of the search widget button
         @type bool
         """
         self.searchWidget.setVisible(checked)
-        
+
         if checked:
             self.searchEditName.setFocus(Qt.FocusReason.OtherFocusReason)
             self.searchEditName.selectAll()
-            
+
             self.__updateSearchActionButtons()
             self.__updateSearchButton()
             self.__updateSearchMoreButton(False)
-    
+
     @pyqtSlot(str)
     def on_searchEditName_textChanged(self, txt):
         """
         Private slot handling a change of the search term.
-        
+
         @param txt search term
         @type str
         """
         self.__updateSearchButton()
-    
+
     @pyqtSlot()
     def on_searchEditName_returnPressed(self):
         """
         Private slot initiating a search via a press of the Return key.
         """
-        if (
-            bool(self.searchEditName.text()) and
-            self.__isPipAvailable()
-        ):
+        if bool(self.searchEditName.text()) and self.__isPipAvailable():
             self.__searchFirst()
-    
+
     @pyqtSlot()
     def on_searchButton_clicked(self):
         """
         Private slot handling a press of the search button.
         """
         self.__searchFirst()
-    
+
     @pyqtSlot()
     def on_searchMoreButton_clicked(self):
         """
         Private slot handling a press of the search more button.
         """
         self.__search(self.__lastSearchPage + 1)
-    
+
     @pyqtSlot()
     def on_searchResultList_itemSelectionChanged(self):
         """
         Private slot handling changes of the search result selection.
         """
         self.__updateSearchActionButtons()
-    
+
     def __searchFirst(self):
         """
         Private method to perform the search for packages.
         """
         self.searchResultList.clear()
         self.searchInfoLabel.clear()
-        
+
         self.__updateSearchMoreButton(False)
-        
+
         self.__search()
-    
+
     def __search(self, page=1):
         """
         Private method to perform the search by calling the PyPI search URL.
-        
+
         @param page search page to retrieve (defaults to 1)
         @type int (optional)
         """
         self.__lastSearchPage = page
-        
+
         self.searchButton.setEnabled(False)
-        
+
         searchTerm = self.searchEditName.text().strip()
         searchTerm = bytes(QUrl.toPercentEncoding(searchTerm)).decode()
         urlQuery = QUrlQuery()
@@ -902,29 +883,29 @@
         urlQuery.addQueryItem("page", str(page))
         url = QUrl(self.__pip.getIndexUrlSearch())
         url.setQuery(urlQuery)
-        
+
         request = QNetworkRequest(QUrl(url))
         request.setAttribute(
             QNetworkRequest.Attribute.CacheLoadControlAttribute,
-            QNetworkRequest.CacheLoadControl.AlwaysNetwork)
+            QNetworkRequest.CacheLoadControl.AlwaysNetwork,
+        )
         reply = self.__pip.getNetworkAccessManager().get(request)
-        reply.finished.connect(
-            lambda: self.__searchResponse(reply))
+        reply.finished.connect(lambda: self.__searchResponse(reply))
         self.__replies.append(reply)
-    
+
     def __searchResponse(self, reply):
         """
         Private method to extract the search result data from the response.
-        
+
         @param reply reference to the reply object containing the data
         @type QNetworkReply
         """
         if reply in self.__replies:
             self.__replies.remove(reply)
-        
+
         urlQuery = QUrlQuery(reply.url())
         searchTerm = urlQuery.queryItemValue("q")
-        
+
         if reply.error() != QNetworkReply.NetworkError.NoError:
             EricMessageBox.warning(
                 None,
@@ -932,78 +913,86 @@
                 self.tr(
                     "<p>Received an error while searching for <b>{0}</b>.</p>"
                     "<p>Error: {1}</p>"
-                ).format(searchTerm, reply.errorString())
+                ).format(searchTerm, reply.errorString()),
             )
             reply.deleteLater()
             return
-        
+
         data = bytes(reply.readAll()).decode()
         reply.deleteLater()
-        
+
         results = PypiSearchResultsParser(data).getResults()
         if results:
             # PyPI returns max. 20 entries per page
             if len(results) < 20:
-                msg = self.tr("%n package(s) found.", "",
-                              (self.__lastSearchPage - 1) * 20 + len(results))
+                msg = self.tr(
+                    "%n package(s) found.",
+                    "",
+                    (self.__lastSearchPage - 1) * 20 + len(results),
+                )
                 self.__updateSearchMoreButton(False)
             else:
                 msg = self.tr("Showing first {0} packages found.").format(
-                    self.__lastSearchPage * 20)
+                    self.__lastSearchPage * 20
+                )
                 self.__updateSearchMoreButton(True)
             self.searchInfoLabel.setText(msg)
             lastItem = self.searchResultList.topLevelItem(
-                self.searchResultList.topLevelItemCount() - 1)
+                self.searchResultList.topLevelItemCount() - 1
+            )
         else:
             self.__updateSearchMoreButton(False)
             if self.__lastSearchPage == 1:
                 EricMessageBox.warning(
                     self,
                     self.tr("Search PyPI"),
-                    self.tr("""<p>There were no results for <b>{0}</b>.</p>""")
-                    .format(searchTerm)
+                    self.tr("""<p>There were no results for <b>{0}</b>.</p>""").format(
+                        searchTerm
+                    ),
                 )
                 self.searchInfoLabel.setText(
-                    self.tr("""<p>There were no results for <b>{0}</b>.</p>""")
-                    .format(searchTerm)
+                    self.tr("""<p>There were no results for <b>{0}</b>.</p>""").format(
+                        searchTerm
+                    )
                 )
             else:
                 EricMessageBox.warning(
                     self,
                     self.tr("Search PyPI"),
-                    self.tr("""<p>There were no more results for"""
-                            """ <b>{0}</b>.</p>""").format(searchTerm)
+                    self.tr(
+                        """<p>There were no more results for""" """ <b>{0}</b>.</p>"""
+                    ).format(searchTerm),
                 )
             lastItem = None
-        
+
         wrapper = textwrap.TextWrapper(width=80)
         for result in results:
             try:
-                description = "\n".join([
-                    wrapper.fill(line) for line in
-                    result['description'].strip().splitlines()
-                ])
+                description = "\n".join(
+                    [
+                        wrapper.fill(line)
+                        for line in result["description"].strip().splitlines()
+                    ]
+                )
             except KeyError:
                 description = ""
-            date = (
-                result["released"]
-                if "released" in result else
-                result["created"]
-            )
+            date = result["released"] if "released" in result else result["created"]
             itm = QTreeWidgetItem(
-                self.searchResultList, [
-                    result['name'].strip(),
-                    result['version'],
+                self.searchResultList,
+                [
+                    result["name"].strip(),
+                    result["version"],
                     date.strip(),
                     description,
-                ])
-            itm.setData(0, self.SearchVersionRole, result['version'])
-        
+                ],
+            )
+            itm.setData(0, self.SearchVersionRole, result["version"])
+
         if lastItem:
             self.searchResultList.scrollToItem(
-                lastItem,
-                QAbstractItemView.ScrollHint.PositionAtTop)
-        
+                lastItem, QAbstractItemView.ScrollHint.PositionAtTop
+            )
+
         header = self.searchResultList.header()
         header.setStretchLastSection(False)
         header.resizeSections(QHeaderView.ResizeMode.ResizeToContents)
@@ -1012,44 +1001,42 @@
             headerSize += header.sectionSize(col)
         if headerSize < header.width():
             header.setStretchLastSection(True)
-        
+
         self.__finishSearch()
-    
+
     def __finishSearch(self):
         """
         Private slot performing the search finishing actions.
         """
         self.__updateSearchActionButtons()
         self.__updateSearchButton()
-        
+
         self.searchEditName.setFocus(Qt.FocusReason.OtherFocusReason)
-    
+
     @pyqtSlot()
     def on_installButton_clicked(self):
         """
         Private slot to handle pressing the Install button..
         """
         packages = [
-            itm.text(0).strip()
-            for itm in self.searchResultList.selectedItems()
+            itm.text(0).strip() for itm in self.searchResultList.selectedItems()
         ]
         self.executeInstallPackages(packages)
-    
+
     @pyqtSlot()
     def on_installUserSiteButton_clicked(self):
         """
         Private slot to handle pressing the Install to User-Site button..
         """
         packages = [
-            itm.text(0).strip()
-            for itm in self.searchResultList.selectedItems()
+            itm.text(0).strip() for itm in self.searchResultList.selectedItems()
         ]
         self.executeInstallPackages(packages, userSite=True)
-    
+
     def executeInstallPackages(self, packages, userSite=False):
         """
         Public method to install the given list of packages.
-        
+
         @param packages list of package names to be installed
         @type list of str
         @param userSite flag indicating to install to the user directory
@@ -1057,52 +1044,51 @@
         """
         venvName = self.environmentsComboBox.currentText()
         if venvName and packages:
-            self.__pip.installPackages(packages, venvName=venvName,
-                                       userSite=userSite)
+            self.__pip.installPackages(packages, venvName=venvName, userSite=userSite)
             self.on_refreshButton_clicked()
-    
+
     @pyqtSlot()
     def on_showDetailsButton_clicked(self):
         """
         Private slot to handle pressing the Show Details button.
         """
         self.__showSearchedDetails()
-    
+
     @pyqtSlot(QTreeWidgetItem, int)
     def on_searchResultList_itemActivated(self, item, column):
         """
         Private slot reacting on an search result item activation.
-        
+
         @param item reference to the activated item
         @type QTreeWidgetItem
         @param column activated column
         @type int
         """
         self.__showSearchedDetails(item)
-    
+
     def __showSearchedDetails(self, item=None):
         """
         Private slot to show details about the selected search result package.
-        
+
         @param item reference to the search result item to show details for
         @type QTreeWidgetItem
         """
         self.showDetailsButton.setEnabled(False)
-        
+
         if not item:
             item = self.searchResultList.selectedItems()[0]
-        
+
         packageVersion = item.data(0, self.SearchVersionRole)
         packageName = item.text(0)
-        
-        self.__showPackageDetails(packageName, packageVersion,
-                                  installable=True)
-    
-    def __showPackageDetails(self, packageName, packageVersion,
-                             upgradable=False, installable=False):
+
+        self.__showPackageDetails(packageName, packageVersion, installable=True)
+
+    def __showPackageDetails(
+        self, packageName, packageVersion, upgradable=False, installable=False
+    ):
         """
         Private method to populate the package details dialog.
-        
+
         @param packageName name of the package to show details for
         @type str
         @param packageVersion version of the package
@@ -1115,43 +1101,44 @@
         @type bool (optional)
         """
         with EricOverrideCursor():
-            packageData = self.__pip.getPackageDetails(
-                packageName, packageVersion)
-        
+            packageData = self.__pip.getPackageDetails(packageName, packageVersion)
+
         if packageData:
             from .PipPackageDetailsDialog import PipPackageDetailsDialog
-            
+
             self.showDetailsButton.setEnabled(True)
-            
+
             if installable:
                 buttonsMode = PipPackageDetailsDialog.ButtonInstall
             elif upgradable:
                 buttonsMode = (
-                    PipPackageDetailsDialog.ButtonRemove |
-                    PipPackageDetailsDialog.ButtonUpgrade
+                    PipPackageDetailsDialog.ButtonRemove
+                    | PipPackageDetailsDialog.ButtonUpgrade
                 )
             else:
                 buttonsMode = PipPackageDetailsDialog.ButtonRemove
-            
+
             if self.__packageDetailsDialog is not None:
                 self.__packageDetailsDialog.close()
-            
-            self.__packageDetailsDialog = (
-                PipPackageDetailsDialog(packageData, buttonsMode=buttonsMode,
-                                        parent=self)
+
+            self.__packageDetailsDialog = PipPackageDetailsDialog(
+                packageData, buttonsMode=buttonsMode, parent=self
             )
             self.__packageDetailsDialog.show()
         else:
             EricMessageBox.warning(
                 self,
                 self.tr("Search PyPI"),
-                self.tr("""<p>No package details info for <b>{0}</b>"""
-                        """ available.</p>""").format(packageName))
-    
+                self.tr(
+                    """<p>No package details info for <b>{0}</b>"""
+                    """ available.</p>"""
+                ).format(packageName),
+            )
+
     #######################################################################
     ## Menu related methods below
     #######################################################################
-        
+
     def __initPipMenu(self):
         """
         Private method to create the super menu and attach it to the super
@@ -1159,81 +1146,80 @@
         """
         self.__pipMenu = QMenu()
         self.__installPipAct = self.__pipMenu.addAction(
-            self.tr("Install Pip"),
-            self.__installPip)
+            self.tr("Install Pip"), self.__installPip
+        )
         self.__installPipUserAct = self.__pipMenu.addAction(
-            self.tr("Install Pip to User-Site"),
-            self.__installPipUser)
+            self.tr("Install Pip to User-Site"), self.__installPipUser
+        )
         self.__repairPipAct = self.__pipMenu.addAction(
-            self.tr("Repair Pip"),
-            self.__repairPip)
+            self.tr("Repair Pip"), self.__repairPip
+        )
         self.__pipMenu.addSeparator()
         self.__installPackagesAct = self.__pipMenu.addAction(
-            self.tr("Install Packages"),
-            self.__installPackages)
+            self.tr("Install Packages"), self.__installPackages
+        )
         self.__installLocalPackageAct = self.__pipMenu.addAction(
-            self.tr("Install Local Package"),
-            self.__installLocalPackage)
+            self.tr("Install Local Package"), self.__installLocalPackage
+        )
         self.__pipMenu.addSeparator()
         self.__installRequirementsAct = self.__pipMenu.addAction(
-            self.tr("Install Requirements"),
-            self.__installRequirements)
+            self.tr("Install Requirements"), self.__installRequirements
+        )
         self.__reinstallPackagesAct = self.__pipMenu.addAction(
-            self.tr("Re-Install Selected Packages"),
-            self.__reinstallPackages)
+            self.tr("Re-Install Selected Packages"), self.__reinstallPackages
+        )
         self.__uninstallRequirementsAct = self.__pipMenu.addAction(
-            self.tr("Uninstall Requirements"),
-            self.__uninstallRequirements)
+            self.tr("Uninstall Requirements"), self.__uninstallRequirements
+        )
         self.__generateRequirementsAct = self.__pipMenu.addAction(
-            self.tr("Generate Requirements..."),
-            self.__generateRequirements)
+            self.tr("Generate Requirements..."), self.__generateRequirements
+        )
         self.__pipMenu.addSeparator()
         self.__showLicensesDialogAct = self.__pipMenu.addAction(
-            self.tr("Show Licenses..."),
-            self.__showLicensesDialog)
+            self.tr("Show Licenses..."), self.__showLicensesDialog
+        )
         self.__pipMenu.addSeparator()
         self.__checkVulnerabilityAct = self.__pipMenu.addAction(
-            self.tr("Check Vulnerabilities"),
-            self.__updateVulnerabilityData)
+            self.tr("Check Vulnerabilities"), self.__updateVulnerabilityData
+        )
         # updateVulnerabilityDbAct
         self.__pipMenu.addAction(
-            self.tr("Update Vulnerability Database"),
-            self.__updateVulnerabilityDbCache)
+            self.tr("Update Vulnerability Database"), self.__updateVulnerabilityDbCache
+        )
         self.__pipMenu.addSeparator()
         self.__cyclonedxAct = self.__pipMenu.addAction(
-            self.tr("Create SBOM file"),
-            self.__createSBOMFile)
+            self.tr("Create SBOM file"), self.__createSBOMFile
+        )
         self.__pipMenu.addSeparator()
         self.__cacheInfoAct = self.__pipMenu.addAction(
-            self.tr("Show Cache Info..."),
-            self.__showCacheInfo)
+            self.tr("Show Cache Info..."), self.__showCacheInfo
+        )
         self.__cacheShowListAct = self.__pipMenu.addAction(
-            self.tr("Show Cached Files..."),
-            self.__showCacheList)
+            self.tr("Show Cached Files..."), self.__showCacheList
+        )
         self.__cacheRemoveAct = self.__pipMenu.addAction(
-            self.tr("Remove Cached Files..."),
-            self.__removeCachedFiles)
+            self.tr("Remove Cached Files..."), self.__removeCachedFiles
+        )
         self.__cachePurgeAct = self.__pipMenu.addAction(
-            self.tr("Purge Cache..."),
-            self.__purgeCache)
+            self.tr("Purge Cache..."), self.__purgeCache
+        )
         self.__pipMenu.addSeparator()
         # editUserConfigAct
         self.__pipMenu.addAction(
-            self.tr("Edit User Configuration..."),
-            self.__editUserConfiguration)
+            self.tr("Edit User Configuration..."), self.__editUserConfiguration
+        )
         self.__editVirtualenvConfigAct = self.__pipMenu.addAction(
             self.tr("Edit Environment Configuration..."),
-            self.__editVirtualenvConfiguration)
+            self.__editVirtualenvConfiguration,
+        )
         self.__pipMenu.addSeparator()
         # pipConfigAct
-        self.__pipMenu.addAction(
-            self.tr("Configure..."),
-            self.__pipConfigure)
+        self.__pipMenu.addAction(self.tr("Configure..."), self.__pipConfigure)
 
         self.__pipMenu.aboutToShow.connect(self.__aboutToShowPipMenu)
-        
+
         self.pipMenuButton.setMenu(self.__pipMenu)
-    
+
     def __aboutToShowPipMenu(self):
         """
         Private slot to set the action enabled status.
@@ -1241,33 +1227,34 @@
         enable = bool(self.environmentsComboBox.currentText())
         enablePip = self.__isPipAvailable()
         enablePipCache = self.__availablePipVersion() >= (20, 1, 0)
-        
+
         self.__installPipAct.setEnabled(not enablePip)
         self.__installPipUserAct.setEnabled(not enablePip)
         self.__repairPipAct.setEnabled(enablePip)
-        
+
         self.__installPackagesAct.setEnabled(enablePip)
         self.__installLocalPackageAct.setEnabled(enablePip)
         self.__reinstallPackagesAct.setEnabled(enablePip)
-        
+
         self.__installRequirementsAct.setEnabled(enablePip)
         self.__uninstallRequirementsAct.setEnabled(enablePip)
         self.__generateRequirementsAct.setEnabled(enablePip)
-        
+
         self.__cacheInfoAct.setEnabled(enablePipCache)
         self.__cacheShowListAct.setEnabled(enablePipCache)
         self.__cacheRemoveAct.setEnabled(enablePipCache)
         self.__cachePurgeAct.setEnabled(enablePipCache)
-        
+
         self.__editVirtualenvConfigAct.setEnabled(enable)
-        
+
         self.__checkVulnerabilityAct.setEnabled(
-            enable & self.vulnerabilityCheckBox.isEnabled())
-        
+            enable & self.vulnerabilityCheckBox.isEnabled()
+        )
+
         self.__cyclonedxAct.setEnabled(enable)
-        
+
         self.__showLicensesDialogAct.setEnabled(enable)
-    
+
     @pyqtSlot()
     def __installPip(self):
         """
@@ -1277,7 +1264,7 @@
         if venvName:
             self.__pip.installPip(venvName)
             self.on_refreshButton_clicked()
-    
+
     @pyqtSlot()
     def __installPipUser(self):
         """
@@ -1288,7 +1275,7 @@
         if venvName:
             self.__pip.installPip(venvName, userSite=True)
             self.on_refreshButton_clicked()
-    
+
     @pyqtSlot()
     def __repairPip(self):
         """
@@ -1299,7 +1286,7 @@
         if venvName:
             self.__pip.repairPip(venvName)
             self.on_refreshButton_clicked()
-    
+
     @pyqtSlot()
     def __installPackages(self):
         """
@@ -1308,11 +1295,12 @@
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             from .PipPackagesInputDialog import PipPackagesInputDialog
+
             dlg = PipPackagesInputDialog(self, self.tr("Install Packages"))
             if dlg.exec() == QDialog.DialogCode.Accepted:
                 packages, user = dlg.getData()
                 self.executeInstallPackages(packages, userSite=user)
-    
+
     @pyqtSlot()
     def __installLocalPackage(self):
         """
@@ -1321,25 +1309,27 @@
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             from .PipFileSelectionDialog import PipFileSelectionDialog
+
             dlg = PipFileSelectionDialog(self, "package")
             if dlg.exec() == QDialog.DialogCode.Accepted:
                 package, user = dlg.getData()
                 if package and os.path.exists(package):
                     self.executeInstallPackages([package], userSite=user)
-    
+
     @pyqtSlot()
     def __reinstallPackages(self):
         """
         Private slot to force a re-installation of the selected packages.
         """
-        packages = [itm.text(PipPackagesWidget.PackageColumn)
-                    for itm in self.packagesList.selectedItems()]
+        packages = [
+            itm.text(PipPackagesWidget.PackageColumn)
+            for itm in self.packagesList.selectedItems()
+        ]
         venvName = self.environmentsComboBox.currentText()
         if venvName and packages:
-            self.__pip.installPackages(packages, venvName=venvName,
-                                       forceReinstall=True)
+            self.__pip.installPackages(packages, venvName=venvName, forceReinstall=True)
             self.on_refreshButton_clicked()
-    
+
     @pyqtSlot()
     def __installRequirements(self):
         """
@@ -1349,7 +1339,7 @@
         if venvName:
             self.__pip.installRequirements(venvName)
             self.on_refreshButton_clicked()
-    
+
     @pyqtSlot()
     def __uninstallRequirements(self):
         """
@@ -1359,7 +1349,7 @@
         if venvName:
             self.__pip.uninstallRequirements(venvName)
             self.on_refreshButton_clicked()
-    
+
     @pyqtSlot()
     def __generateRequirements(self):
         """
@@ -1368,17 +1358,18 @@
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             from .PipFreezeDialog import PipFreezeDialog
+
             self.__freezeDialog = PipFreezeDialog(self.__pip, self)
             self.__freezeDialog.show()
             self.__freezeDialog.start(venvName)
-    
+
     @pyqtSlot()
     def __editUserConfiguration(self):
         """
         Private slot to edit the user configuration.
         """
         self.__editConfiguration()
-    
+
     @pyqtSlot()
     def __editVirtualenvConfiguration(self):
         """
@@ -1387,15 +1378,16 @@
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             self.__editConfiguration(venvName=venvName)
-    
+
     def __editConfiguration(self, venvName=""):
         """
         Private method to edit a configuration.
-        
+
         @param venvName name of the environment to act upon
         @type str
         """
         from QScintilla.MiniEditor import MiniEditor
+
         if venvName:
             cfgFile = self.__pip.getVirtualenvConfig(venvName)
             if not cfgFile:
@@ -1407,10 +1399,10 @@
             EricMessageBox.critical(
                 None,
                 self.tr("Edit Configuration"),
-                self.tr("""No valid configuration path determined."""
-                        """ Aborting"""))
+                self.tr("""No valid configuration path determined.""" """ Aborting"""),
+            )
             return
-        
+
         try:
             if not os.path.isdir(cfgDir):
                 os.makedirs(cfgDir)
@@ -1418,32 +1410,32 @@
             EricMessageBox.critical(
                 None,
                 self.tr("Edit Configuration"),
-                self.tr("""No valid configuration path determined."""
-                        """ Aborting"""))
+                self.tr("""No valid configuration path determined.""" """ Aborting"""),
+            )
             return
-        
+
         if not os.path.exists(cfgFile):
             with contextlib.suppress(OSError), open(cfgFile, "w") as f:
                 f.write("[global]\n")
-        
+
         # check, if the destination is writeable
         if not os.access(cfgFile, os.W_OK):
             EricMessageBox.critical(
                 None,
                 self.tr("Edit Configuration"),
-                self.tr("""No valid configuration path determined."""
-                        """ Aborting"""))
+                self.tr("""No valid configuration path determined.""" """ Aborting"""),
+            )
             return
-        
+
         self.__editor = MiniEditor(cfgFile, "Properties")
         self.__editor.show()
- 
+
     def __pipConfigure(self):
         """
         Private slot to open the configuration page.
         """
         ericApp().getObject("UserInterface").showPreferences("pipPage")
-    
+
     @pyqtSlot()
     def __showCacheInfo(self):
         """
@@ -1452,7 +1444,7 @@
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             self.__pip.showCacheInfo(venvName)
-    
+
     @pyqtSlot()
     def __showCacheList(self):
         """
@@ -1461,7 +1453,7 @@
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             self.__pip.cacheList(venvName)
-    
+
     @pyqtSlot()
     def __removeCachedFiles(self):
         """
@@ -1470,7 +1462,7 @@
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             self.__pip.cacheRemove(venvName)
-    
+
     @pyqtSlot()
     def __purgeCache(self):
         """
@@ -1479,25 +1471,26 @@
         venvName = self.environmentsComboBox.currentText()
         if venvName:
             self.__pip.cachePurge(venvName)
-    
+
     ##################################################################
     ## Interface to the vulnerability checks below
     ##################################################################
-    
+
     @pyqtSlot(bool)
     def on_vulnerabilityCheckBox_clicked(self, checked):
         """
         Private slot handling a change of the automatic vulnerability checks.
-        
+
         @param checked flag indicating the state of the check box
         @type bool
         """
         if checked:
             self.__updateVulnerabilityData(clearFirst=True)
-        
+
         self.packagesList.header().setSectionHidden(
-            PipPackagesWidget.VulnerabilityColumn, not checked)
-    
+            PipPackagesWidget.VulnerabilityColumn, not checked
+        )
+
     @pyqtSlot()
     def __clearVulnerabilityInfo(self):
         """
@@ -1508,64 +1501,66 @@
             itm.setText(PipPackagesWidget.VulnerabilityColumn, "")
             itm.setToolTip(PipPackagesWidget.VulnerabilityColumn, "")
             itm.setIcon(PipPackagesWidget.VulnerabilityColumn, QIcon())
-            itm.setData(PipPackagesWidget.VulnerabilityColumn,
-                        PipPackagesWidget.VulnerabilityRole,
-                        None)
-    
+            itm.setData(
+                PipPackagesWidget.VulnerabilityColumn,
+                PipPackagesWidget.VulnerabilityRole,
+                None,
+            )
+
     @pyqtSlot()
     def __updateVulnerabilityData(self, clearFirst=True):
         """
         Private slot to update the shown vulnerability info.
-        
+
         @param clearFirst flag indicating to clear the vulnerability info first
             (defaults to True)
         @type bool (optional)
         """
         if clearFirst:
             self.__clearVulnerabilityInfo()
-        
+
         packages = []
         for row in range(self.packagesList.topLevelItemCount()):
             itm = self.packagesList.topLevelItem(row)
-            packages.append(Package(
-                name=itm.text(PipPackagesWidget.PackageColumn),
-                version=itm.text(PipPackagesWidget.InstalledVersionColumn)
-            ))
-        
-        error, vulnerabilities = (
-            self.__pip.getVulnerabilityChecker().check(packages)
-        )
+            packages.append(
+                Package(
+                    name=itm.text(PipPackagesWidget.PackageColumn),
+                    version=itm.text(PipPackagesWidget.InstalledVersionColumn),
+                )
+            )
+
+        error, vulnerabilities = self.__pip.getVulnerabilityChecker().check(packages)
         if error == VulnerabilityCheckError.OK:
             for package in vulnerabilities:
                 items = self.packagesList.findItems(
-                    package,
-                    Qt.MatchFlag.MatchExactly |
-                    Qt.MatchFlag.MatchCaseSensitive
+                    package, Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive
                 )
                 if items:
                     itm = items[0]
                     itm.setData(
                         PipPackagesWidget.VulnerabilityColumn,
                         PipPackagesWidget.VulnerabilityRole,
-                        vulnerabilities[package]
+                        vulnerabilities[package],
                     )
                     affected = {v.spec for v in vulnerabilities[package]}
                     itm.setText(
-                        PipPackagesWidget.VulnerabilityColumn,
-                        ', '.join(affected)
+                        PipPackagesWidget.VulnerabilityColumn, ", ".join(affected)
                     )
                     itm.setIcon(
                         PipPackagesWidget.VulnerabilityColumn,
-                        UI.PixmapCache.getIcon("securityLow")
+                        UI.PixmapCache.getIcon("securityLow"),
                     )
-        
-        elif error in (VulnerabilityCheckError.FullDbUnavailable,
-                       VulnerabilityCheckError.SummaryDbUnavailable):
+
+        elif error in (
+            VulnerabilityCheckError.FullDbUnavailable,
+            VulnerabilityCheckError.SummaryDbUnavailable,
+        ):
             self.vulnerabilityCheckBox.setChecked(False)
             self.vulnerabilityCheckBox.setEnabled(False)
             self.packagesList.setColumnHidden(
-                PipPackagesWidget.VulnerabilityColumn, True)
-    
+                PipPackagesWidget.VulnerabilityColumn, True
+            )
+
     @pyqtSlot()
     def __updateVulnerabilityDbCache(self):
         """
@@ -1574,12 +1569,13 @@
         """
         with EricOverrideCursor():
             self.__pip.getVulnerabilityChecker().updateVulnerabilityDb()
-    
-    def __showVulnerabilityInformation(self, packageName, packageVersion,
-                                       vulnerabilities):
+
+    def __showVulnerabilityInformation(
+        self, packageName, packageVersion, vulnerabilities
+    ):
         """
         Private method to show the detected vulnerability data.
-        
+
         @param packageName name of the package
         @type str
         @param packageVersion installed version number
@@ -1587,9 +1583,8 @@
         @param vulnerabilities list of vulnerabilities
         @type list of Vulnerability
         """
-        header = (
-            self.tr("{0} {1}", "package name, package version")
-            .format(packageName, packageVersion)
+        header = self.tr("{0} {1}", "package name, package version").format(
+            packageName, packageVersion
         )
         topItem = QTreeWidgetItem(self.infoWidget, [header])
         topItem.setFirstColumnSpanned(True)
@@ -1597,78 +1592,76 @@
         font = topItem.font(0)
         font.setBold(True)
         topItem.setFont(0, font)
-        
+
         for vulnerability in vulnerabilities:
             title = (
                 vulnerability.cve
-                if vulnerability.cve else
-                vulnerability.vulnerabilityId
+                if vulnerability.cve
+                else vulnerability.vulnerabilityId
             )
             titleItem = QTreeWidgetItem(topItem, [title])
             titleItem.setFirstColumnSpanned(True)
             titleItem.setExpanded(True)
-            
+
             QTreeWidgetItem(
-                titleItem,
-                [self.tr("Affected Version:"), vulnerability.spec])
+                titleItem, [self.tr("Affected Version:"), vulnerability.spec]
+            )
             itm = QTreeWidgetItem(
-                titleItem,
-                [self.tr("Advisory:"), vulnerability.advisory])
-            itm.setToolTip(1, "<p>{0}</p>".format(
-                vulnerability.advisory.replace("\r\n", "<br/>")
-            ))
-        
+                titleItem, [self.tr("Advisory:"), vulnerability.advisory]
+            )
+            itm.setToolTip(
+                1, "<p>{0}</p>".format(vulnerability.advisory.replace("\r\n", "<br/>"))
+            )
+
         self.infoWidget.scrollToTop()
         self.infoWidget.resizeColumnToContents(0)
-        
+
         header = self.infoWidget.header()
         header.setStretchLastSection(True)
-    
+
     #######################################################################
     ## Dependency tree related methods below
     #######################################################################
-    
+
     @pyqtSlot(bool)
     def on_viewToggleButton_toggled(self, checked):
         """
         Private slot handling the view selection.
-        
+
         @param checked state of the toggle button
         @type bool
         """
         if checked:
-            self.viewsStackWidget.setCurrentWidget(
-                self.dependenciesPage)
+            self.viewsStackWidget.setCurrentWidget(self.dependenciesPage)
             self.__refreshDependencyTree()
         else:
-            self.viewsStackWidget.setCurrentWidget(
-                self.packagesPage)
+            self.viewsStackWidget.setCurrentWidget(self.packagesPage)
             self.__refreshPackagesList()
-    
+
     @pyqtSlot(bool)
     def on_requiresButton_toggled(self, checked):
         """
         Private slot handling the selection of the view type.
-        
+
         @param checked state of the radio button (unused)
         @type bool
         """
         self.__refreshDependencyTree()
-    
+
     @pyqtSlot()
     def on_localDepCheckBox_clicked(self):
         """
         Private slot handling the switching of the local mode.
         """
         self.__refreshDependencyTree()
-    
+
     @pyqtSlot()
     def on_userDepCheckBox_clicked(self):
         """
         Private slot handling the switching of the 'user-site' mode.
         """
         self.__refreshDependencyTree()
-    
+
     def __refreshDependencyTree(self):
         """
         Private method to refresh the dependency tree.
@@ -1685,80 +1678,81 @@
                         usersite=self.userDepCheckBox.isChecked(),
                         reverse=self.requiredByButton.isChecked(),
                     )
-                    
+
                     self.dependenciesList.setUpdatesEnabled(False)
                     for dependency in dependencies:
                         self.__addDependency(dependency, self.dependenciesList)
-                    
+
                     self.dependenciesList.sortItems(
-                        PipPackagesWidget.DepPackageColumn,
-                        Qt.SortOrder.AscendingOrder)
+                        PipPackagesWidget.DepPackageColumn, Qt.SortOrder.AscendingOrder
+                    )
                     for col in range(self.dependenciesList.columnCount()):
                         self.dependenciesList.resizeColumnToContents(col)
                     self.dependenciesList.setUpdatesEnabled(True)
-        
+
         self.__updateDepActionButtons()
-    
+
     def __addDependency(self, dependency, parent):
         """
         Private method to add a dependency branch to a given parent.
-        
+
         @param dependency dependency to be added
         @type dict
         @param parent reference to the parent item
         @type QTreeWidget or QTreeWidgetItem
         """
-        itm = QTreeWidgetItem(parent, [
-            dependency["package_name"],
-            dependency["installed_version"],
-            dependency["required_version"],
-        ])
+        itm = QTreeWidgetItem(
+            parent,
+            [
+                dependency["package_name"],
+                dependency["installed_version"],
+                dependency["required_version"],
+            ],
+        )
         itm.setExpanded(True)
-        
+
         if dependency["installed_version"] == "?":
-            itm.setText(PipPackagesWidget.DepInstalledVersionColumn,
-                        self.tr("unknown"))
-        
+            itm.setText(PipPackagesWidget.DepInstalledVersionColumn, self.tr("unknown"))
+
         if dependency["required_version"].lower() not in ("any", "?"):
             spec = (
                 "=={0}".format(dependency["required_version"])
-                if dependency["required_version"][0] in "0123456789" else
-                dependency["required_version"]
+                if dependency["required_version"][0] in "0123456789"
+                else dependency["required_version"]
             )
             specifierSet = SpecifierSet(specifiers=spec)
             if not specifierSet.contains(dependency["installed_version"]):
-                itm.setIcon(PipPackagesWidget.DepRequiredVersionColumn,
-                            UI.PixmapCache.getIcon("warning"))
-        
+                itm.setIcon(
+                    PipPackagesWidget.DepRequiredVersionColumn,
+                    UI.PixmapCache.getIcon("warning"),
+                )
+
         elif dependency["required_version"].lower() == "any":
-            itm.setText(PipPackagesWidget.DepRequiredVersionColumn,
-                        self.tr("any"))
-        
+            itm.setText(PipPackagesWidget.DepRequiredVersionColumn, self.tr("any"))
+
         elif dependency["required_version"] == "?":
-            itm.setText(PipPackagesWidget.DepRequiredVersionColumn,
-                        self.tr("unknown"))
-        
+            itm.setText(PipPackagesWidget.DepRequiredVersionColumn, self.tr("unknown"))
+
         # recursively add sub-dependencies
         for dep in dependency["dependencies"]:
             self.__addDependency(dep, itm)
-    
+
     @pyqtSlot(QTreeWidgetItem, int)
     def on_dependenciesList_itemActivated(self, item, column):
         """
         Private slot reacting on a package item of the dependency tree being
         activated.
-        
+
         @param item reference to the activated item
         @type QTreeWidgetItem
         @param column activated column
         @type int
         """
         packageName = item.text(PipPackagesWidget.DepPackageColumn)
-        packageVersion = item.text(
-            PipPackagesWidget.DepInstalledVersionColumn)
-        
+        packageVersion = item.text(PipPackagesWidget.DepInstalledVersionColumn)
+
         self.__showPackageDetails(packageName, packageVersion)
-    
+
     @pyqtSlot()
     def on_dependenciesList_itemSelectionChanged(self):
         """
@@ -1767,28 +1761,27 @@
         """
         if len(self.dependenciesList.selectedItems()) == 0:
             self.dependencyInfoWidget.clear()
-    
+
     @pyqtSlot(QTreeWidgetItem, int)
     def on_dependenciesList_itemPressed(self, item, column):
         """
         Private slot reacting on a package item of the dependency tree being
         pressed.
-        
+
         @param item reference to the pressed item
         @type QTreeWidgetItem
         @param column pressed column
         @type int
         """
         self.dependencyInfoWidget.clear()
-        
+
         if item is not None:
             self.__showPackageInformation(
-                item.text(PipPackagesWidget.DepPackageColumn),
-                self.dependencyInfoWidget
+                item.text(PipPackagesWidget.DepPackageColumn), self.dependencyInfoWidget
             )
-        
+
         self.__updateDepActionButtons()
-    
+
     @pyqtSlot()
     def on_refreshDependenciesButton_clicked(self):
         """
@@ -1797,19 +1790,19 @@
         currentEnvironment = self.environmentsComboBox.currentText()
         self.environmentsComboBox.clear()
         self.dependenciesList.clear()
-        
+
         with EricOverrideCursor():
             self.__populateEnvironments()
-            
+
             index = self.environmentsComboBox.findText(
                 currentEnvironment,
-                Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive
+                Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive,
             )
             if index != -1:
                 self.environmentsComboBox.setCurrentIndex(index)
-        
+
         self.__updateDepActionButtons()
-    
+
     @pyqtSlot()
     def on_showDepPackageDetailsButton_clicked(self):
         """
@@ -1819,24 +1812,22 @@
         item = self.dependenciesList.selectedItems()[0]
         if item:
             packageName = item.text(PipPackagesWidget.DepPackageColumn)
-            packageVersion = item.text(
-                PipPackagesWidget.DepInstalledVersionColumn)
-            
+            packageVersion = item.text(PipPackagesWidget.DepInstalledVersionColumn)
+
             self.__showPackageDetails(packageName, packageVersion)
-    
+
     def __updateDepActionButtons(self):
         """
         Private method to set the state of the dependency page action buttons.
         """
         self.showDepPackageDetailsButton.setEnabled(
-            len(self.dependenciesList.selectedItems()) == 1 and
-            self.__isPipAvailable()
+            len(self.dependenciesList.selectedItems()) == 1 and self.__isPipAvailable()
         )
-    
+
     ##################################################################
     ## Interface to show the licenses dialog below
     ##################################################################
-    
+
     @pyqtSlot()
     def __showLicensesDialog(self):
         """
@@ -1844,38 +1835,38 @@
         environment.
         """
         from .PipLicensesDialog import PipLicensesDialog
-        
+
         environment = self.environmentsComboBox.currentText()
         localPackages = (
             self.localDepCheckBox.isChecked()
-            if self.viewToggleButton.isChecked() else
-            self.localCheckBox.isChecked()
+            if self.viewToggleButton.isChecked()
+            else self.localCheckBox.isChecked()
         )
         usersite = (
             self.userDepCheckBox.isChecked()
-            if self.viewToggleButton.isChecked() else
-            self.userCheckBox.isChecked()
+            if self.viewToggleButton.isChecked()
+            else self.userCheckBox.isChecked()
         )
         dlg = PipLicensesDialog(
             self.__pip,
             environment,
             localPackages=localPackages,
             usersite=usersite,
-            parent=self
+            parent=self,
         )
         dlg.exec()
-    
+
     ##################################################################
     ## Interface to create a SBOM file using CycloneDX
     ##################################################################
-    
+
     @pyqtSlot()
     def __createSBOMFile(self):
         """
         Private slot to create a "Software Bill Of Material" file.
         """
         import CycloneDXInterface
-        
+
         venvName = self.environmentsComboBox.currentText()
         if venvName == self.__pip.getProjectEnvironmentString():
             venvName = "<project>"

eric ide

mercurial