eric7/PipInterface/PipPackagesWidget.py

branch
eric7
changeset 8978
38c3ddf21537
parent 8977
663521af48b2
child 8985
30e9e592732d
equal deleted inserted replaced
8977:663521af48b2 8978:38c3ddf21537
22 22
23 from EricWidgets.EricApplication import ericApp 23 from EricWidgets.EricApplication import ericApp
24 from EricWidgets import EricMessageBox 24 from EricWidgets import EricMessageBox
25 from EricGui.EricOverrideCursor import EricOverrideCursor 25 from EricGui.EricOverrideCursor import EricOverrideCursor
26 26
27 from .PipVulnerabilityChecker import Package, VulnerabilityCheckError
27 from .Ui_PipPackagesWidget import Ui_PipPackagesWidget 28 from .Ui_PipPackagesWidget import Ui_PipPackagesWidget
28 29
29 import UI.PixmapCache 30 import UI.PixmapCache
30 import Globals 31 import Globals
31 import Preferences 32 import Preferences
303 pipList = self.packagesList.findItems( 304 pipList = self.packagesList.findItems(
304 "pip", 305 "pip",
305 Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive 306 Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive
306 ) 307 )
307 if len(pipList) > 0: 308 if len(pipList) > 0:
308 pipVersionTuple = Globals.versionToTuple(pipList[0].text(1)) 309 pipVersionTuple = Globals.versionToTuple(
310 pipList[0].text(PipPackagesWidget.InstalledVersionColumn))
309 311
310 return pipVersionTuple 312 return pipVersionTuple
311 313
312 def getPip(self): 314 def getPip(self):
313 """ 315 """
463 """ 465 """
464 Private slot handling the switching of the 'user-site' mode. 466 Private slot handling the switching of the 'user-site' mode.
465 """ 467 """
466 self.__refreshPackagesList() 468 self.__refreshPackagesList()
467 469
468 @pyqtSlot() 470 def __showPackageInformation(self, packageName):
469 def on_packagesList_itemSelectionChanged(self): 471 """
470 """ 472 Private method to show information for a package.
471 Private slot handling the selection of a package. 473
474 @param packageName name of the package
475 @type str
476 """
477 environment = self.environmentsComboBox.currentText()
478 interpreter = self.__pip.getVirtualenvInterpreter(environment)
479 if not interpreter:
480 return
481
482 args = ["-m", "pip", "show"]
483 if self.verboseCheckBox.isChecked():
484 args.append("--verbose")
485 if self.installedFilesCheckBox.isChecked():
486 args.append("--files")
487 args.append(packageName)
488
489 with EricOverrideCursor():
490 success, output = self.__pip.runProcess(args, interpreter)
491
492 if success and output:
493 mode = self.ShowProcessGeneralMode
494 for line in output.splitlines():
495 line = line.rstrip()
496 if line != "---":
497 if mode != self.ShowProcessGeneralMode:
498 if line[0] == " ":
499 QTreeWidgetItem(
500 self.infoWidget,
501 [" ", line.strip()])
502 else:
503 mode = self.ShowProcessGeneralMode
504 if mode == self.ShowProcessGeneralMode:
505 try:
506 label, info = line.split(": ", 1)
507 except ValueError:
508 label = line[:-1]
509 info = ""
510 label = label.lower()
511 if label in self.__infoLabels:
512 QTreeWidgetItem(
513 self.infoWidget,
514 [self.__infoLabels[label], info])
515 if label == "files":
516 mode = self.ShowProcessFilesListMode
517 elif label == "classifiers":
518 mode = self.ShowProcessClassifiersMode
519 elif label == "entry-points":
520 mode = self.ShowProcessEntryPointsMode
521 self.infoWidget.scrollToTop()
522
523 header = self.infoWidget.header()
524 header.setStretchLastSection(False)
525 header.resizeSections(QHeaderView.ResizeMode.ResizeToContents)
526 if (
527 header.sectionSize(0) + header.sectionSize(1) <
528 header.width()
529 ):
530 header.setStretchLastSection(True)
531
532 @pyqtSlot(QTreeWidgetItem, int)
533 def on_packagesList_itemClicked(self, item, column):
534 """
535 Private slot reacting on a package item click.
536
537 @param item reference to the clicked item
538 @type QTreeWidgetItem
539 @param column clicked column
540 @type int
472 """ 541 """
473 self.infoWidget.clear() 542 self.infoWidget.clear()
474 543
475 if len(self.packagesList.selectedItems()) == 1: 544 if (
476 itm = self.packagesList.selectedItems()[0] 545 column == PipPackagesWidget.VulnerabilityColumn and
477 546 bool(item.text(PipPackagesWidget.VulnerabilityColumn))
478 environment = self.environmentsComboBox.currentText() 547 ):
479 interpreter = self.__pip.getVirtualenvInterpreter(environment) 548 self.__showVulnerabilityInformation(
480 if not interpreter: 549 item.text(PipPackagesWidget.PackageColumn),
481 return 550 item.text(PipPackagesWidget.InstalledVersionColumn),
482 551 item.data(PipPackagesWidget.VulnerabilityColumn,
483 args = ["-m", "pip", "show"] 552 PipPackagesWidget.VulnerabilityRole)
484 if self.verboseCheckBox.isChecked(): 553 )
485 args.append("--verbose") 554 else:
486 if self.installedFilesCheckBox.isChecked(): 555 self.__showPackageInformation(
487 args.append("--files") 556 item.text(PipPackagesWidget.PackageColumn)
488 args.append(itm.text(0)) 557 )
489
490 with EricOverrideCursor():
491 success, output = self.__pip.runProcess(args, interpreter)
492
493 if success and output:
494 mode = self.ShowProcessGeneralMode
495 for line in output.splitlines():
496 line = line.rstrip()
497 if line != "---":
498 if mode != self.ShowProcessGeneralMode:
499 if line[0] == " ":
500 QTreeWidgetItem(
501 self.infoWidget,
502 [" ", line.strip()])
503 else:
504 mode = self.ShowProcessGeneralMode
505 if mode == self.ShowProcessGeneralMode:
506 try:
507 label, info = line.split(": ", 1)
508 except ValueError:
509 label = line[:-1]
510 info = ""
511 label = label.lower()
512 if label in self.__infoLabels:
513 QTreeWidgetItem(
514 self.infoWidget,
515 [self.__infoLabels[label], info])
516 if label == "files":
517 mode = self.ShowProcessFilesListMode
518 elif label == "classifiers":
519 mode = self.ShowProcessClassifiersMode
520 elif label == "entry-points":
521 mode = self.ShowProcessEntryPointsMode
522 self.infoWidget.scrollToTop()
523
524 header = self.infoWidget.header()
525 header.setStretchLastSection(False)
526 header.resizeSections(QHeaderView.ResizeMode.ResizeToContents)
527 if (
528 header.sectionSize(0) + header.sectionSize(1) <
529 header.width()
530 ):
531 header.setStretchLastSection(True)
532 558
533 self.__updateActionButtons() 559 self.__updateActionButtons()
534 560
535 @pyqtSlot(QTreeWidgetItem, int) 561 @pyqtSlot(QTreeWidgetItem, int)
536 def on_packagesList_itemActivated(self, item, column): 562 def on_packagesList_itemDoubleClicked(self, item, column):
537 """ 563 """
538 Private slot reacting on a package item activation. 564 Private slot reacting on a package item double click.
539 565
540 @param item reference to the activated item 566 @param item reference to the double clicked item
541 @type QTreeWidgetItem 567 @type QTreeWidgetItem
542 @param column activated column 568 @param column double clicked column
543 @type int 569 @type int
544 """ 570 """
545 packageName = item.text(0) 571 packageName = item.text(PipPackagesWidget.PackageColumn)
546 upgradable = bool(item.text(2)) 572 upgradable = bool(item.text(PipPackagesWidget.AvailableVersionColumn))
547 if column == 1: 573 if column == PipPackagesWidget.InstalledVersionColumn:
548 # show details for installed version 574 # show details for installed version
549 packageVersion = item.text(1) 575 packageVersion = item.text(
576 PipPackagesWidget.InstalledVersionColumn)
550 else: 577 else:
551 # show details for available version or installed one 578 # show details for available version or installed one
552 if item.text(2): 579 if item.text(PipPackagesWidget.AvailableVersionColumn):
553 packageVersion = item.text(2) 580 packageVersion = item.text(
581 PipPackagesWidget.AvailableVersionColumn)
554 else: 582 else:
555 packageVersion = item.text(1) 583 packageVersion = item.text(
584 PipPackagesWidget.InstalledVersionColumn)
556 585
557 self.__showPackageDetails(packageName, packageVersion, 586 self.__showPackageDetails(packageName, packageVersion,
558 upgradable=upgradable) 587 upgradable=upgradable)
559 588
560 @pyqtSlot(bool) 589 @pyqtSlot(bool)
564 checkbox. 593 checkbox.
565 594
566 @param checked state of the checkbox 595 @param checked state of the checkbox
567 @type bool 596 @type bool
568 """ 597 """
569 self.on_packagesList_itemSelectionChanged() 598 self.on_packagesList_itemClicked(self.packagesList.currentItem(),
599 self.packagesList.currentColumn())
570 600
571 @pyqtSlot(bool) 601 @pyqtSlot(bool)
572 def on_installedFilesCheckBox_clicked(self, checked): 602 def on_installedFilesCheckBox_clicked(self, checked):
573 """ 603 """
574 Private slot to handle a change of the installed files information 604 Private slot to handle a change of the installed files information
575 checkbox. 605 checkbox.
576 606
577 @param checked state of the checkbox 607 @param checked state of the checkbox
578 @type bool 608 @type bool
579 """ 609 """
580 self.on_packagesList_itemSelectionChanged() 610 self.on_packagesList_itemClicked(self.packagesList.currentItem(),
611 self.packagesList.currentColumn())
581 612
582 @pyqtSlot() 613 @pyqtSlot()
583 def on_refreshButton_clicked(self): 614 def on_refreshButton_clicked(self):
584 """ 615 """
585 Private slot to refresh the display. 616 Private slot to refresh the display.
603 @pyqtSlot() 634 @pyqtSlot()
604 def on_upgradeButton_clicked(self): 635 def on_upgradeButton_clicked(self):
605 """ 636 """
606 Private slot to upgrade selected packages of the selected environment. 637 Private slot to upgrade selected packages of the selected environment.
607 """ 638 """
608 packages = [itm.text(0) for itm in self.__selectedUpdateableItems()] 639 packages = [itm.text(PipPackagesWidget.PackageColumn)
640 for itm in self.__selectedUpdateableItems()]
609 if packages: 641 if packages:
610 self.executeUpgradePackages(packages) 642 self.executeUpgradePackages(packages)
611 643
612 @pyqtSlot() 644 @pyqtSlot()
613 def on_upgradeAllButton_clicked(self): 645 def on_upgradeAllButton_clicked(self):
614 """ 646 """
615 Private slot to upgrade all packages of the selected environment. 647 Private slot to upgrade all packages of the selected environment.
616 """ 648 """
617 packages = [itm.text(0) for itm in self.__allUpdateableItems()] 649 packages = [itm.text(PipPackagesWidget.PackageColumn)
650 for itm in self.__allUpdateableItems()]
618 if packages: 651 if packages:
619 self.executeUpgradePackages(packages) 652 self.executeUpgradePackages(packages)
620 653
621 @pyqtSlot() 654 @pyqtSlot()
622 def on_uninstallButton_clicked(self): 655 def on_uninstallButton_clicked(self):
660 Private slot to show information for the selected package. 693 Private slot to show information for the selected package.
661 """ 694 """
662 item = self.packagesList.selectedItems()[0] 695 item = self.packagesList.selectedItems()[0]
663 if item: 696 if item:
664 packageName = item.text(PipPackagesWidget.PackageColumn) 697 packageName = item.text(PipPackagesWidget.PackageColumn)
665 upgradable = bool(item.text(2)) 698 upgradable = bool(item.text(
699 PipPackagesWidget.AvailableVersionColumn))
666 # show details for available version or installed one 700 # show details for available version or installed one
667 if item.text(2): 701 if item.text(PipPackagesWidget.AvailableVersionColumn):
668 packageVersion = item.text(2) 702 packageVersion = item.text(
703 PipPackagesWidget.AvailableVersionColumn)
669 else: 704 else:
670 packageVersion = item.text(1) 705 packageVersion = item.text(
706 PipPackagesWidget.InstalledVersionColumn)
671 707
672 self.__showPackageDetails(packageName, packageVersion, 708 self.__showPackageDetails(packageName, packageVersion,
673 upgradable=upgradable) 709 upgradable=upgradable)
674 710
675 ####################################################################### 711 #######################################################################
1022 self.__uninstallRequirements) 1058 self.__uninstallRequirements)
1023 self.__generateRequirementsAct = self.__pipMenu.addAction( 1059 self.__generateRequirementsAct = self.__pipMenu.addAction(
1024 self.tr("Generate Requirements..."), 1060 self.tr("Generate Requirements..."),
1025 self.__generateRequirements) 1061 self.__generateRequirements)
1026 self.__pipMenu.addSeparator() 1062 self.__pipMenu.addSeparator()
1063 self.__checkVulnerabilityAct = self.__pipMenu.addAction(
1064 self.tr("Check Vulnerabilities"),
1065 self.__updateVulnerabilityData)
1066 # updateVulnerabilityDbAct
1067 self.__pipMenu.addAction(
1068 self.tr("Update Vulnerability Database"),
1069 self.__updateVulnerabilityDbCache)
1070 self.__pipMenu.addSeparator()
1027 self.__cacheInfoAct = self.__pipMenu.addAction( 1071 self.__cacheInfoAct = self.__pipMenu.addAction(
1028 self.tr("Show Cache Info..."), 1072 self.tr("Show Cache Info..."),
1029 self.__showCacheInfo) 1073 self.__showCacheInfo)
1030 self.__cacheShowListAct = self.__pipMenu.addAction( 1074 self.__cacheShowListAct = self.__pipMenu.addAction(
1031 self.tr("Show Cached Files..."), 1075 self.tr("Show Cached Files..."),
1078 self.__cacheShowListAct.setEnabled(enablePipCache) 1122 self.__cacheShowListAct.setEnabled(enablePipCache)
1079 self.__cacheRemoveAct.setEnabled(enablePipCache) 1123 self.__cacheRemoveAct.setEnabled(enablePipCache)
1080 self.__cachePurgeAct.setEnabled(enablePipCache) 1124 self.__cachePurgeAct.setEnabled(enablePipCache)
1081 1125
1082 self.__editVirtualenvConfigAct.setEnabled(enable) 1126 self.__editVirtualenvConfigAct.setEnabled(enable)
1127 self.__checkVulnerabilityAct.setEnabled(
1128 enable & self.vulnerabilityCheckBox.isEnabled())
1083 1129
1084 @pyqtSlot() 1130 @pyqtSlot()
1085 def __installPip(self): 1131 def __installPip(self):
1086 """ 1132 """
1087 Private slot to install pip into the selected environment. 1133 Private slot to install pip into the selected environment.
1332 @type bool (optional) 1378 @type bool (optional)
1333 """ 1379 """
1334 if clearFirst: 1380 if clearFirst:
1335 self.__clearVulnerabilityInfo() 1381 self.__clearVulnerabilityInfo()
1336 1382
1337 packages = [] # TODO: fill this list with real data 1383 packages = []
1384 for row in range(self.packagesList.topLevelItemCount()):
1385 itm = self.packagesList.topLevelItem(row)
1386 packages.append(Package(
1387 name=itm.text(PipPackagesWidget.PackageColumn),
1388 version=itm.text(PipPackagesWidget.InstalledVersionColumn)
1389 ))
1338 1390
1339 error, vulnerabilities = ( 1391 error, vulnerabilities = (
1340 self.__pip.getVulnerabilityChecker().check(packages) 1392 self.__pip.getVulnerabilityChecker().check(packages)
1341 ) 1393 )
1394 if error == VulnerabilityCheckError.OK:
1395 for package in vulnerabilities:
1396 items = self.packagesList.findItems(
1397 package,
1398 Qt.MatchFlag.MatchExactly |
1399 Qt.MatchFlag.MatchCaseSensitive
1400 )
1401 if items:
1402 itm = items[0]
1403 itm.setData(
1404 PipPackagesWidget.VulnerabilityColumn,
1405 PipPackagesWidget.VulnerabilityRole,
1406 vulnerabilities[package]
1407 )
1408 affected = {v.spec for v in vulnerabilities[package]}
1409 itm.setText(
1410 PipPackagesWidget.VulnerabilityColumn,
1411 ', '.join(affected)
1412 )
1413 itm.setIcon(
1414 PipPackagesWidget.VulnerabilityColumn,
1415 UI.PixmapCache.getIcon("securityLow")
1416 )
1417
1418 elif error in (VulnerabilityCheckError.FullDbUnavailable,
1419 VulnerabilityCheckError.SummaryDbUnavailable):
1420 self.vulnerabilityCheckBox.setChecked(False)
1421 self.vulnerabilityCheckBox.setEnabled(False)
1422 self.packagesList.setColumnHidden(
1423 PipPackagesWidget.VulnerabilityColumn, True)
1424
1425 @pyqtSlot()
1426 def __updateVulnerabilityDbCache(self):
1427 """
1428 Private slot to initiate an update of the local cache of the
1429 vulnerability database.
1430 """
1431 with EricOverrideCursor():
1432 self.__pip.getVulnerabilityChecker().updateVulnerabilityDb()
1433
1434 def __showVulnerabilityInformation(self, packageName, packageVersion,
1435 vulnerabilities):
1436 """
1437 Private method to show the detected vulnerability data.
1438
1439 @param packageName name of the package
1440 @type str
1441 @param packageVersion installed version number
1442 @type str
1443 @param vulnerabilities list of vulnerabilities
1444 @type list of Vulnerability
1445 """
1446 header = (
1447 self.tr("{0} {1}", "package name, package version")
1448 .format(packageName, packageVersion)
1449 )
1450 topItem = QTreeWidgetItem(self.infoWidget, [header])
1451 topItem.setFirstColumnSpanned(True)
1452 topItem.setExpanded(True)
1453 font = topItem.font(0)
1454 font.setBold(True)
1455 topItem.setFont(0, font)
1456
1457 for vulnerability in vulnerabilities:
1458 title = (
1459 vulnerability.cve
1460 if vulnerability.cve else
1461 vulnerability.vulnerabilityId
1462 )
1463 titleItem = QTreeWidgetItem(topItem, [title])
1464 titleItem.setFirstColumnSpanned(True)
1465 titleItem.setExpanded(True)
1466
1467 QTreeWidgetItem(
1468 titleItem,
1469 [self.tr("Affected Version:"), vulnerability.spec])
1470 itm = QTreeWidgetItem(
1471 titleItem,
1472 [self.tr("Advisory:"), vulnerability.advisory])
1473 itm.setToolTip(1, "<p>{0}</p>".format(
1474 vulnerability.advisory.replace("\r\n", "<br/>")
1475 ))
1476
1477 self.infoWidget.scrollToTop()
1478 self.infoWidget.resizeColumnToContents(0)
1479
1480 header = self.infoWidget.header()
1481 header.setStretchLastSection(True)

eric ide

mercurial