--- a/src/eric7/PipInterface/PipPackagesWidget.py Sun Jul 02 17:40:17 2023 +0200 +++ b/src/eric7/PipInterface/PipPackagesWidget.py Tue Aug 01 09:59:45 2023 +0200 @@ -158,6 +158,7 @@ ShowProcessClassifiersMode = 1 ShowProcessEntryPointsMode = 2 ShowProcessFilesListMode = 3 + ShowProcessUrlsListMode = 4 SearchVersionRole = Qt.ItemDataRole.UserRole + 1 VulnerabilityRole = Qt.ItemDataRole.UserRole + 2 @@ -212,6 +213,7 @@ self.refreshDependenciesButton.setIcon(EricPixmapCache.getIcon("reload")) self.showDepPackageDetailsButton.setIcon(EricPixmapCache.getIcon("info")) self.dependencyRepairButton.setIcon(EricPixmapCache.getIcon("repair")) + self.dependencyRepairAllButton.setIcon(EricPixmapCache.getIcon("repairAll")) self.__pip = pip @@ -223,22 +225,24 @@ ) self.__infoLabels = { - "name": self.tr("Name:"), - "version": self.tr("Version:"), - "location": self.tr("Location:"), - "requires": self.tr("Requires:"), - "summary": self.tr("Summary:"), - "home-page": self.tr("Homepage:"), "author": self.tr("Author:"), "author-email": self.tr("Author Email:"), - "license": self.tr("License:"), - "metadata-version": self.tr("Metadata Version:"), - "installer": self.tr("Installer:"), "classifiers": self.tr("Classifiers:"), "entry-points": self.tr("Entry Points:"), "files": self.tr("Files:"), + "home-page": self.tr("Homepage:"), + "installer": self.tr("Installer:"), + "license": self.tr("License:"), + "location": self.tr("Location:"), + "metadata-version": self.tr("Metadata Version:"), + "name": self.tr("Name:"), + "project-urls": self.tr("Project URLs:"), + "requires": self.tr("Requires:"), + "required-by": self.tr("Required By:"), + "summary": self.tr("Summary:"), + "version": self.tr("Version:"), } - self.infoWidget.setHeaderLabels(["Key", "Value"]) + self.packageInfoWidget.setHeaderLabels(["Key", "Value"]) self.dependencyInfoWidget.setHeaderLabels(["Key", "Value"]) venvManager = ericApp().getObject("VirtualEnvManager") @@ -251,6 +255,8 @@ project.projectOpened.connect(self.__projectOpened) project.projectClosed.connect(self.__projectClosed) + self.__packageDetailsDialog = None + self.__initPipMenu() self.__populateEnvironments() self.__updateActionButtons() @@ -265,9 +271,8 @@ self.__replies = [] - self.__packageDetailsDialog = None - self.viewsStackWidget.setCurrentWidget(self.packagesPage) + self.on_packagesList_currentItemChanged(None, None) @pyqtSlot() def __projectOpened(self): @@ -494,15 +499,18 @@ @type str """ if name != self.__selectedEnvironment: - if not name: - self.environmentPathLabel.setPath("") - self.searchNameEdit.clear() - self.searchNameEdit.setEnabled(False) - else: + if name: self.environmentPathLabel.setPath( self.__pip.getVirtualenvInterpreter(name) ) self.searchNameEdit.setEnabled(True) + else: + self.environmentPathLabel.setPath("") + self.searchNameEdit.clear() + self.searchNameEdit.setEnabled(False) + self.searchResultList.clear() + if self.__packageDetailsDialog is not None: + self.__packageDetailsDialog.close() if self.viewToggleButton.isChecked(): self.__refreshDependencyTree() @@ -582,6 +590,8 @@ mode = self.ShowProcessClassifiersMode elif label == "entry-points": mode = self.ShowProcessEntryPointsMode + elif label == "project-urls": + mode = self.ShowProcessUrlsListMode infoWidget.scrollToTop() header = infoWidget.header() @@ -596,36 +606,42 @@ Private slot reacting on a change of selected items. """ if len(self.packagesList.selectedItems()) == 0: - self.infoWidget.clear() + self.packageInfoWidget.clear() + self.vulnerabilitiesInfoWidget.clear() + self.infoWidget.tabBar().hide() - @pyqtSlot(QTreeWidgetItem, int) - def on_packagesList_itemPressed(self, item, column): + @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) + def on_packagesList_currentItemChanged(self, curr, prev): """ - Private slot reacting on a package item being pressed. + Private slot to handle a change of the current item. - @param item reference to the pressed item + @param curr new current item + @type QTreeWidgetItem + @param prev previous current item @type QTreeWidgetItem - @param column pressed column - @type int """ - self.infoWidget.clear() + self.packageInfoWidget.clear() + self.vulnerabilitiesInfoWidget.clear() - if item is not None: - if column == PipPackagesWidget.VulnerabilityColumn and bool( - item.text(PipPackagesWidget.VulnerabilityColumn) - ): + if curr is None: + self.infoWidget.tabBar().hide() + else: + self.__showPackageInformation( + curr.text(PipPackagesWidget.PackageColumn), self.packageInfoWidget + ) + if bool(curr.text(PipPackagesWidget.VulnerabilityColumn)): self.__showVulnerabilityInformation( - item.text(PipPackagesWidget.PackageColumn), - item.text(PipPackagesWidget.InstalledVersionColumn), - item.data( + curr.text(PipPackagesWidget.PackageColumn), + curr.text(PipPackagesWidget.InstalledVersionColumn), + curr.data( PipPackagesWidget.VulnerabilityColumn, PipPackagesWidget.VulnerabilityRole, ), ) + self.infoWidget.tabBar().show() else: - self.__showPackageInformation( - item.text(PipPackagesWidget.PackageColumn), self.infoWidget - ) + self.infoWidget.tabBar().hide() + self.infoWidget.setCurrentIndex(0) self.__updateActionButtons() @@ -676,9 +692,7 @@ @param checked state of the checkbox @type bool """ - self.on_packagesList_itemPressed( - self.packagesList.currentItem(), self.packagesList.currentColumn() - ) + self.on_packagesList_currentItemChanged(self.packagesList.currentItem(), None) @pyqtSlot(bool) def on_installedFilesCheckBox_clicked(self, checked): @@ -689,9 +703,7 @@ @param checked state of the checkbox @type bool """ - self.on_packagesList_itemPressed( - self.packagesList.currentItem(), self.packagesList.currentColumn() - ) + self.on_packagesList_currentItemChanged(self.packagesList.currentItem(), None) @pyqtSlot() def on_refreshButton_clicked(self): @@ -1733,41 +1745,47 @@ @param vulnerabilities list of vulnerabilities @type list of Vulnerability """ - header = self.tr("{0} {1}", "package name, package version").format( - packageName, packageVersion - ) - topItem = QTreeWidgetItem(self.infoWidget, [header]) - topItem.setFirstColumnSpanned(True) - topItem.setExpanded(True) - 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 vulnerabilities: + header = self.tr("{0} {1}", "package name, package version").format( + packageName, packageVersion ) - titleItem = QTreeWidgetItem(topItem, [title]) - titleItem.setFirstColumnSpanned(True) - titleItem.setExpanded(True) + topItem = QTreeWidgetItem(self.vulnerabilitiesInfoWidget, [header]) + topItem.setFirstColumnSpanned(True) + topItem.setExpanded(True) + font = topItem.font(0) + font.setBold(True) + topItem.setFont(0, font) + + for vulnerability in vulnerabilities: + title = ( + vulnerability.cve + if vulnerability.cve + else vulnerability.vulnerabilityId + ) + titleItem = QTreeWidgetItem(topItem, [title]) + titleItem.setFirstColumnSpanned(True) + titleItem.setExpanded(True) - QTreeWidgetItem( - 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/>")) - ) + QTreeWidgetItem( + 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/>") + ), + ) - self.infoWidget.scrollToTop() - self.infoWidget.resizeColumnToContents(0) + self.vulnerabilitiesInfoWidget.scrollToTop() + self.vulnerabilitiesInfoWidget.resizeColumnToContents(0) - header = self.infoWidget.header() - header.setStretchLastSection(True) + header = self.vulnerabilitiesInfoWidget.header() + header.setStretchLastSection(True) + else: + self.vulnerabilitiesInfoWidget.clear() ####################################################################### ## Dependency tree related methods below @@ -1989,15 +2007,24 @@ ) ) + itm = self.dependenciesList.topLevelItem(0) + while itm: + if not itm.icon(PipPackagesWidget.DepRequiredVersionColumn).isNull(): + self.dependencyRepairAllButton.setEnabled(True) + break + itm = self.dependenciesList.itemBelow(itm) + else: + self.dependencyRepairAllButton.setEnabled(False) + @pyqtSlot() def on_dependencyRepairButton_clicked(self): """ Private slot to repair all selected dependencies. """ - packages = [] + packages = set() for itm in self.dependenciesList.selectedItems(): if not itm.icon(PipPackagesWidget.DepRequiredVersionColumn).isNull(): - packages.append( + packages.add( "{0}{1}".format( itm.text(PipPackagesWidget.DepPackageColumn), itm.text(PipPackagesWidget.DepRequiredVersionColumn), @@ -2007,7 +2034,35 @@ venvName = self.environmentsComboBox.currentText() if venvName and packages: self.__pip.installPackages( - packages, venvName=venvName, userSite=self.userDepCheckBox.isChecked() + list(packages), + venvName=venvName, + userSite=self.userDepCheckBox.isChecked(), + ) + self.on_refreshDependenciesButton_clicked() + + @pyqtSlot() + def on_dependencyRepairAllButton_clicked(self): + """ + Private slot to repair all dependencies. + """ + packages = set() + itm = self.dependenciesList.topLevelItem(0) + while itm: + if not itm.icon(PipPackagesWidget.DepRequiredVersionColumn).isNull(): + packages.add( + "{0}{1}".format( + itm.text(PipPackagesWidget.DepPackageColumn), + itm.text(PipPackagesWidget.DepRequiredVersionColumn), + ) + ) + itm = self.dependenciesList.itemBelow(itm) + + venvName = self.environmentsComboBox.currentText() + if venvName and packages: + self.__pip.installPackages( + list(packages), + venvName=venvName, + userSite=self.userDepCheckBox.isChecked(), ) self.on_refreshDependenciesButton_clicked()