diff -r dce4b2b95c3f -r 1146cc8fbf5d src/eric7/PipInterface/PipPackagesWidget.py --- a/src/eric7/PipInterface/PipPackagesWidget.py Fri Sep 01 10:26:51 2023 +0200 +++ b/src/eric7/PipInterface/PipPackagesWidget.py Thu Oct 05 09:59:53 2023 +0200 @@ -274,6 +274,8 @@ self.viewsStackWidget.setCurrentWidget(self.packagesPage) self.on_packagesList_currentItemChanged(None, None) + self.preferencesChanged() # perform preferences dependent configuration + @pyqtSlot() def __projectOpened(self): """ @@ -292,10 +294,10 @@ @type bool """ if not shutdown: + # the project entry is always at index 1 if self.environmentsComboBox.currentIndex() == 1: self.environmentsComboBox.setCurrentIndex(0) - # the project entry is always at index 1 self.environmentsComboBox.removeItem(1) def __populateEnvironments(self): @@ -441,7 +443,6 @@ with EricOverrideCursor(): # 1. populate with installed packages - self.packagesList.setUpdatesEnabled(False) installedPackages = self.__pip.getInstalledPackages( venvName, localPackages=self.localCheckBox.isChecked(), @@ -450,40 +451,60 @@ ) for package, version in installedPackages: QTreeWidgetItem(self.packagesList, [package, version, "", ""]) - self.packagesList.setUpdatesEnabled(True) + self.packagesList.sortItems( + PipPackagesWidget.PackageColumn, Qt.SortOrder.AscendingOrder + ) + self.packagesList.resizeColumnToContents( + PipPackagesWidget.PackageColumn + ) + self.packagesList.resizeColumnToContents( + PipPackagesWidget.InstalledVersionColumn + ) + QApplication.processEvents() + + # 2. update with vulnerability information + if self.vulnerabilityCheckBox.isChecked(): + self.__updateVulnerabilityData() + self.packagesList.resizeColumnToContents( + PipPackagesWidget.VulnerabilityColumn + ) self.statusLabel.setText(self.tr("Getting outdated packages...")) QApplication.processEvents() - # 2. update with update information - self.packagesList.setUpdatesEnabled(False) - outdatedPackages = self.__pip.getOutdatedPackages( + # 3. update with update information + self.__pip.getOutdatedPackages( venvName, localPackages=self.localCheckBox.isChecked(), notRequired=self.notRequiredCheckBox.isChecked(), usersite=self.userCheckBox.isChecked(), + callback=self.__updateOutdatedInfo, ) - for package, _version, latest in outdatedPackages: - items = self.packagesList.findItems( - package, - Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive, - ) - if items: - itm = items[0] - itm.setText( - PipPackagesWidget.AvailableVersionColumn, latest - ) + + else: + self.__updateActionButtons() + self.__updateSearchActionButtons() + self.__updateSearchButton() + self.__updateSearchMoreButton(False) + + def __updateOutdatedInfo(self, outdatedPackages): + """ + Private method to process the list of outdated packages. - 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() + @param outdatedPackages list of tuples containing the package name, + installed version and available version + @type list of tuple of (str, str, str) + """ + for package, _version, latest in outdatedPackages: + items = self.packagesList.findItems( + package, + Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive, + ) + if items: + items[0].setText(PipPackagesWidget.AvailableVersionColumn, latest) + self.packagesList.resizeColumnToContents( + PipPackagesWidget.AvailableVersionColumn + ) + self.statusLabel.hide() self.__updateActionButtons() self.__updateSearchActionButtons() @@ -610,6 +631,8 @@ self.vulnerabilitiesInfoWidget.clear() self.infoWidget.tabBar().hide() + self.__updateActionButtons() + @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) def on_packagesList_currentItemChanged(self, curr, prev): """ @@ -1316,7 +1339,7 @@ self.tr("Check Vulnerabilities"), self.__checkVulnerability ) # updateVulnerabilityDbAct - self.__pipMenu.addAction( + self.__updateVulnerabilitiesAct = self.__pipMenu.addAction( self.tr("Update Vulnerability Database"), self.__updateVulnerabilityDbCache ) self.__pipMenu.addSeparator() @@ -1364,7 +1387,12 @@ self.__editVirtualenvConfigAct.setEnabled(enable) self.__checkVulnerabilityAct.setEnabled( - enable & self.vulnerabilityCheckBox.isEnabled() + enable + and self.vulnerabilityCheckBox.isEnabled() + and Preferences.getPip("VulnerabilityCheckEnabled") + ) + self.__updateVulnerabilitiesAct.setEnabled( + enable and Preferences.getPip("VulnerabilityCheckEnabled") ) self.__cyclonedxAct.setEnabled(enable) @@ -1589,6 +1617,7 @@ if dlg.result() == QDialog.DialogCode.Accepted: dlg.setPreferences() Preferences.syncPreferences() + self.preferencesChanged() @pyqtSlot() def __showCacheInfo(self): @@ -1631,6 +1660,22 @@ ################################################################## @pyqtSlot(bool) + def setVulnerabilityEnabled(self, enable): + """ + Public slot to set the enabled state of the vulnerability checks. + + @param enable vulnerability checks enabled state + @type bool + """ + self.vulnerabilityCheckBox.setChecked(enable) + self.vulnerabilityCheckBox.setEnabled(enable) + self.packagesList.setColumnHidden( + PipPackagesWidget.VulnerabilityColumn, not enable + ) + if not enable: + self.__clearVulnerabilityInfo() + + @pyqtSlot(bool) def on_vulnerabilityCheckBox_clicked(self, checked): """ Private slot handling a change of the automatic vulnerability checks. @@ -1640,6 +1685,8 @@ """ if checked: self.__updateVulnerabilityData(clearFirst=True) + else: + self.__clearVulnerabilityInfo() self.packagesList.header().setSectionHidden( PipPackagesWidget.VulnerabilityColumn, not checked @@ -1691,37 +1738,37 @@ ) ) - 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 - ) - if items: - itm = items[0] - itm.setData( - PipPackagesWidget.VulnerabilityColumn, - PipPackagesWidget.VulnerabilityRole, - vulnerabilities[package], + if packages: + 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, ) - affected = {v.spec for v in vulnerabilities[package]} - itm.setText( - PipPackagesWidget.VulnerabilityColumn, ", ".join(affected) - ) - itm.setIcon( - PipPackagesWidget.VulnerabilityColumn, - EricPixmapCache.getIcon("securityLow"), - ) + if items: + itm = items[0] + itm.setData( + PipPackagesWidget.VulnerabilityColumn, + PipPackagesWidget.VulnerabilityRole, + vulnerabilities[package], + ) + affected = {v.spec for v in vulnerabilities[package]} + itm.setText( + PipPackagesWidget.VulnerabilityColumn, ", ".join(affected) + ) + itm.setIcon( + PipPackagesWidget.VulnerabilityColumn, + EricPixmapCache.getIcon("securityLow"), + ) - elif error in ( - VulnerabilityCheckError.FullDbUnavailable, - VulnerabilityCheckError.SummaryDbUnavailable, - ): - self.vulnerabilityCheckBox.setChecked(False) - self.vulnerabilityCheckBox.setEnabled(False) - self.packagesList.setColumnHidden( - PipPackagesWidget.VulnerabilityColumn, True - ) + elif error in ( + VulnerabilityCheckError.FullDbUnavailable, + VulnerabilityCheckError.SummaryDbUnavailable, + ): + self.setVulnerabilityEnabled(False) @pyqtSlot() def __updateVulnerabilityDbCache(self): @@ -2102,3 +2149,22 @@ if venvName == self.__pip.getProjectEnvironmentString(): venvName = "<project>" CycloneDXInterface.createCycloneDXFile(venvName) + + ################################################################## + ## Interface to preferences + ################################################################## + + @pyqtSlot() + def preferencesChanged(self): + """ + Public slot to handle a change of preferences. + """ + enable = self.setVulnerabilityEnabled( + Preferences.getPip("VulnerabilityCheckEnabled") + ) + if enable != self.vulnerabilityCheckBox.isEnabled(): + # only if status changes because it is an expensive operation + if self.vulnerabilityCheckBox.isChecked(): + self.__updateVulnerabilityData(clearFirst=True) + else: + self.__clearVulnerabilityInfo()