diff -r ca442cd49b9e -r 663521af48b2 eric7/PipInterface/PipPackagesWidget.py --- a/eric7/PipInterface/PipPackagesWidget.py Sun Mar 13 15:20:26 2022 +0100 +++ b/eric7/PipInterface/PipPackagesWidget.py Sun Mar 13 19:59:03 2022 +0100 @@ -13,6 +13,7 @@ import contextlib from PyQt6.QtCore import pyqtSlot, Qt, QUrl, QUrlQuery +from PyQt6.QtGui import QIcon from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest from PyQt6.QtWidgets import ( QWidget, QToolButton, QApplication, QHeaderView, QTreeWidgetItem, @@ -148,6 +149,12 @@ ShowProcessFilesListMode = 3 SearchVersionRole = Qt.ItemDataRole.UserRole + 1 + VulnerabilityRole = Qt.ItemDataRole.UserRole + 2 + + PackageColumn = 0 + InstalledVersionColumn = 1 + AvailableVersionColumn = 2 + VulnerabilityColumn = 3 def __init__(self, pip, parent=None): """ @@ -189,7 +196,7 @@ self.__pip = pip self.packagesList.header().setSortIndicator( - 0, Qt.SortOrder.AscendingOrder) + PipPackagesWidget.PackageColumn, Qt.SortOrder.AscendingOrder) self.__infoLabels = { "name": self.tr("Name:"), @@ -324,7 +331,7 @@ """ return [ itm for itm in self.packagesList.selectedItems() - if bool(itm.text(2)) + if bool(itm.text(PipPackagesWidget.AvailableVersionColumn)) ] def __allUpdateableItems(self): @@ -337,7 +344,7 @@ updateableItems = [] for index in range(self.packagesList.topLevelItemCount()): itm = self.packagesList.topLevelItem(index) - if itm.text(2): + if itm.text(PipPackagesWidget.AvailableVersionColumn): updateableItems.append(itm) return updateableItems @@ -384,7 +391,8 @@ 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...")) @@ -406,12 +414,20 @@ ) if items: itm = items[0] - itm.setText(2, latest) + itm.setText( + PipPackagesWidget.AvailableVersionColumn, + latest) - self.packagesList.sortItems(0, Qt.SortOrder.AscendingOrder) + self.packagesList.sortItems( + 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() @@ -428,33 +444,24 @@ """ self.__refreshPackagesList() - @pyqtSlot(bool) - def on_localCheckBox_clicked(self, checked): + @pyqtSlot() + def on_localCheckBox_clicked(self): """ Private slot handling the switching of the local mode. - - @param checked state of the local check box - @type bool """ self.__refreshPackagesList() - @pyqtSlot(bool) - def on_notRequiredCheckBox_clicked(self, checked): + @pyqtSlot() + def on_notRequiredCheckBox_clicked(self): """ Private slot handling the switching of the 'not required' mode. - - @param checked state of the 'not required' check box - @type bool """ self.__refreshPackagesList() - @pyqtSlot(bool) - def on_userCheckBox_clicked(self, checked): + @pyqtSlot() + def on_userCheckBox_clicked(self): """ Private slot handling the switching of the 'user-site' mode. - - @param checked state of the 'user-site' check box - @type bool """ self.__refreshPackagesList() @@ -616,7 +623,8 @@ """ Private slot to remove selected packages of the selected environment. """ - packages = [itm.text(0) for itm in self.packagesList.selectedItems()] + packages = [itm.text(PipPackagesWidget.PackageColumn) + for itm in self.packagesList.selectedItems()] self.executeUninstallPackages(packages) def executeUninstallPackages(self, packages): @@ -653,7 +661,7 @@ """ item = self.packagesList.selectedItems()[0] if item: - packageName = item.text(0) + packageName = item.text(PipPackagesWidget.PackageColumn) upgradable = bool(item.text(2)) # show details for available version or installed one if item.text(2): @@ -1137,7 +1145,8 @@ """ Private slot to force a re-installation of the selected packages. """ - packages = [itm.text(0) 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, @@ -1283,3 +1292,50 @@ 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) + + @pyqtSlot() + def __clearVulnerabilityInfo(self): + """ + Private slot to clear the vulnerability info. + """ + for row in range(self.packagesList.topLevelItemCount()): + itm = self.packagesList.topLevelItem(row) + itm.setText(PipPackagesWidget.VulnerabilityColumn, "") + itm.setToolTip(PipPackagesWidget.VulnerabilityColumn, "") + itm.setIcon(PipPackagesWidget.VulnerabilityColumn, QIcon()) + 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 = [] # TODO: fill this list with real data + + error, vulnerabilities = ( + self.__pip.getVulnerabilityChecker().check(packages) + )