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) |
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..."), |
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) |