src/eric7/PluginManager/PluginRepositoryDialog.py

branch
eric7
changeset 10314
1f7d52f024b1
parent 10069
435cc5875135
child 10439
21c28b0f9e41
equal deleted inserted replaced
10313:8f69edb4ad73 10314:1f7d52f024b1
5 5
6 """ 6 """
7 Module implementing a dialog showing the available plugins. 7 Module implementing a dialog showing the available plugins.
8 """ 8 """
9 9
10 import enum
10 import glob 11 import glob
11 import os 12 import os
12 import re 13 import re
13 import zipfile 14 import zipfile
15
16 from collections import ChainMap, defaultdict
14 17
15 from PyQt6.QtCore import ( 18 from PyQt6.QtCore import (
16 QCoreApplication, 19 QCoreApplication,
17 QFile, 20 QFile,
18 QIODevice, 21 QIODevice,
64 67
65 from .PluginManager import PluginManager 68 from .PluginManager import PluginManager
66 from .Ui_PluginRepositoryDialog import Ui_PluginRepositoryDialog 69 from .Ui_PluginRepositoryDialog import Ui_PluginRepositoryDialog
67 70
68 71
72 class PluginStatus(enum.Enum):
73 """
74 Class defining the various plugin status.
75 """
76
77 UpToDate = 0
78 New = 1
79 LocalUpdate = 2
80 RemoteUpdate = 3
81 Error = 4
82
83
69 class PluginRepositoryWidget(QWidget, Ui_PluginRepositoryDialog): 84 class PluginRepositoryWidget(QWidget, Ui_PluginRepositoryDialog):
70 """ 85 """
71 Class implementing a dialog showing the available plugins. 86 Class implementing a dialog showing the available plugins.
72 87
73 @signal closeAndInstall() emitted when the Close & Install button is 88 @signal closeAndInstall() emitted when the Close & Install button is
78 93
79 DescrRole = Qt.ItemDataRole.UserRole 94 DescrRole = Qt.ItemDataRole.UserRole
80 UrlRole = Qt.ItemDataRole.UserRole + 1 95 UrlRole = Qt.ItemDataRole.UserRole + 1
81 FilenameRole = Qt.ItemDataRole.UserRole + 2 96 FilenameRole = Qt.ItemDataRole.UserRole + 2
82 AuthorRole = Qt.ItemDataRole.UserRole + 3 97 AuthorRole = Qt.ItemDataRole.UserRole + 3
83
84 PluginStatusUpToDate = 0
85 PluginStatusNew = 1
86 PluginStatusLocalUpdate = 2
87 PluginStatusRemoteUpdate = 3
88 PluginStatusError = 4
89 98
90 def __init__(self, pluginManager, integrated=False, parent=None): 99 def __init__(self, pluginManager, integrated=False, parent=None):
91 """ 100 """
92 Constructor 101 Constructor
93 102
109 self.__pluginManager = pluginManager 118 self.__pluginManager = pluginManager
110 self.__external = False 119 self.__external = False
111 120
112 self.__integratedWidget = integrated 121 self.__integratedWidget = integrated
113 122
123 self.__statusTranslations = {
124 "stable": self.tr("Stable"),
125 "unstable": self.tr("Unstable"),
126 "obsolete": self.tr("Obsolete"),
127 "unknown": self.tr("Unknown"),
128 }
129 self.__initHeaderItemsCache()
130
114 if self.__integratedWidget: 131 if self.__integratedWidget:
115 self.layout().setContentsMargins(0, 3, 0, 0) 132 self.layout().setContentsMargins(0, 3, 0, 0)
116 133
117 self.__actionButtonsLayout = QHBoxLayout() 134 self.__actionButtonsLayout = QHBoxLayout()
118 self.__actionButtonsLayout.addStretch() 135 self.__actionButtonsLayout.addStretch()
270 @pyqtSlot(QAbstractButton) 287 @pyqtSlot(QAbstractButton)
271 def on_buttonBox_clicked(self, button): 288 def on_buttonBox_clicked(self, button):
272 """ 289 """
273 Private slot to handle the click of a button of the button box. 290 Private slot to handle the click of a button of the button box.
274 291
275 @param button reference to the button pressed (QAbstractButton) 292 @param button reference to the button pressed
293 @type QAbstractButton
276 """ 294 """
277 if button == self.__updateButton: 295 if button == self.__updateButton:
278 self.__updateList() 296 self.__updateList()
279 elif button == self.__downloadButton: 297 elif button == self.__downloadButton:
280 self.__downloadButtonClicked() 298 self.__downloadButtonClicked()
304 322
305 def __formatDescription(self, lines): 323 def __formatDescription(self, lines):
306 """ 324 """
307 Private method to format the description. 325 Private method to format the description.
308 326
309 @param lines lines of the description (list of strings) 327 @param lines lines of the description
310 @return formatted description (string) 328 @type list of str
329 @return formatted description
330 @rtype str
311 """ 331 """
312 # remove empty line at start and end 332 # remove empty line at start and end
313 newlines = lines[:] 333 newlines = lines[:]
314 if len(newlines) and newlines[0] == "": 334 if len(newlines) and newlines[0] == "":
315 del newlines[0] 335 del newlines[0]
331 Private method to change the scheme of the given URL. 351 Private method to change the scheme of the given URL.
332 352
333 @param url URL to be modified 353 @param url URL to be modified
334 @type str 354 @type str
335 @param newScheme scheme to be set for the given URL 355 @param newScheme scheme to be set for the given URL
356 @type str
336 @return modified URL 357 @return modified URL
337 @rtype str 358 @rtype str
338 """ 359 """
339 if not newScheme: 360 if not newScheme:
340 newScheme = ( 361 newScheme = (
348 @pyqtSlot(QPoint) 369 @pyqtSlot(QPoint)
349 def on_repositoryList_customContextMenuRequested(self, pos): 370 def on_repositoryList_customContextMenuRequested(self, pos):
350 """ 371 """
351 Private slot to show the context menu. 372 Private slot to show the context menu.
352 373
353 @param pos position to show the menu (QPoint) 374 @param pos position to show the menu
375 @type QPoint
354 """ 376 """
355 self.__hideAct.setEnabled( 377 self.__hideAct.setEnabled(
356 self.repositoryList.currentItem() is not None 378 self.repositoryList.currentItem() is not None
357 and len(self.__selectedItems()) == 1 379 and len(self.__selectedItems()) == 1
358 ) 380 )
363 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) 385 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
364 def on_repositoryList_currentItemChanged(self, current, previous): 386 def on_repositoryList_currentItemChanged(self, current, previous):
365 """ 387 """
366 Private slot to handle the change of the current item. 388 Private slot to handle the change of the current item.
367 389
368 @param current reference to the new current item (QTreeWidgetItem) 390 @param current reference to the new current item
369 @param previous reference to the old current item (QTreeWidgetItem) 391 @type QTreeWidgetItem
392 @param previous reference to the old current item
393 @type QTreeWidgetItem
370 """ 394 """
371 if self.__repositoryMissing or current is None: 395 if self.__repositoryMissing or current is None:
372 self.descriptionEdit.clear() 396 self.descriptionEdit.clear()
373 self.authorEdit.clear() 397 self.authorEdit.clear()
374 return 398 return
387 current.data(0, PluginRepositoryWidget.AuthorRole) or "" 411 current.data(0, PluginRepositoryWidget.AuthorRole) or ""
388 ) 412 )
389 413
390 def __selectedItems(self): 414 def __selectedItems(self):
391 """ 415 """
392 Private method to get all selected items without the toplevel ones. 416 Private method to get all selected items without the status and category items.
393 417
394 @return list of selected items (list) 418 @return list of selected items without header items
395 """ 419 @rtype list of QTreeWidgetItem
396 ql = self.repositoryList.selectedItems() 420 """
397 for index in range(self.repositoryList.topLevelItemCount()): 421 selectedItems = []
398 ti = self.repositoryList.topLevelItem(index) 422 allCategoryItems = ChainMap(*self.__categoryItems.values())
399 if ti in ql: 423 for itm in self.repositoryList.selectedItems():
400 ql.remove(ti) 424 if (
401 return ql 425 itm not in self.__statusItems.values()
426 and itm not in allCategoryItems.values()
427 ):
428 selectedItems.append(itm)
429 return selectedItems
402 430
403 @pyqtSlot() 431 @pyqtSlot()
404 def on_repositoryList_itemSelectionChanged(self): 432 def on_repositoryList_itemSelectionChanged(self):
405 """ 433 """
406 Private slot to handle a change of the selection. 434 Private slot to handle a change of the selection.
424 url = self.repositoryUrlEdit.text() 452 url = self.repositoryUrlEdit.text()
425 if Preferences.getPluginManager("ForceHttpPluginDownload"): 453 if Preferences.getPluginManager("ForceHttpPluginDownload"):
426 url = url.replace("https://", "http://") 454 url = url.replace("https://", "http://")
427 self.__pluginManager.downLoadRepositoryFile(url=url) 455 self.__pluginManager.downLoadRepositoryFile(url=url)
428 456
429 def __downloadRepositoryFileDone(self, status, filename): # noqa: U100
430 """
431 Private method called after the repository file was downloaded.
432
433 @param status flaging indicating a successful download (boolean)
434 @param filename full path of the downloaded file (string)
435 """
436 self.__populateList()
437
438 def __downloadPluginDone(self, status, filename): 457 def __downloadPluginDone(self, status, filename):
439 """ 458 """
440 Private method called, when the download of a plugin is finished. 459 Private method called, when the download of a plugin is finished.
441 460
442 @param status flag indicating a successful download (boolean) 461 @param status flag indicating a successful download
443 @param filename full path of the downloaded file (string) 462 @type bool
463 @param filename full path of the downloaded file
464 @type str
444 """ 465 """
445 if status: 466 if status:
446 self.__pluginsDownloaded.append(filename) 467 self.__pluginsDownloaded.append(filename)
447 if self.__isDownloadInstall: 468 if self.__isDownloadInstall:
448 self.__allDownloadedOk &= status 469 self.__allDownloadedOk &= status
478 newScheme = ( 499 newScheme = (
479 "http:" 500 "http:"
480 if Preferences.getPluginManager("ForceHttpPluginDownload") 501 if Preferences.getPluginManager("ForceHttpPluginDownload")
481 else self.repositoryUrlEdit.text().split("//", 1)[0] 502 else self.repositoryUrlEdit.text().split("//", 1)[0]
482 ) 503 )
483 for itm in self.repositoryList.selectedItems(): 504 for itm in self.__selectedItems():
484 if itm not in [ 505 url = self.__changeScheme(
485 self.__stableItem, 506 itm.data(0, PluginRepositoryWidget.UrlRole), newScheme
486 self.__unstableItem, 507 )
487 self.__unknownItem, 508 filename = os.path.join(
488 self.__obsoleteItem, 509 Preferences.getPluginManager("DownloadPath"),
489 ]: 510 itm.data(0, PluginRepositoryWidget.FilenameRole),
490 url = self.__changeScheme( 511 )
491 itm.data(0, PluginRepositoryWidget.UrlRole), newScheme 512 self.__pluginsToDownload.append((url, filename))
492 )
493 filename = os.path.join(
494 Preferences.getPluginManager("DownloadPath"),
495 itm.data(0, PluginRepositoryWidget.FilenameRole),
496 )
497 self.__pluginsToDownload.append((url, filename))
498 if self.__pluginsToDownload: 513 if self.__pluginsToDownload:
499 self.__downloadPlugin() 514 self.__downloadPlugin()
500 515
501 def __downloadPluginsDone(self): 516 def __downloadPluginsDone(self):
502 """ 517 """
530 545
531 def __resortRepositoryList(self): 546 def __resortRepositoryList(self):
532 """ 547 """
533 Private method to resort the tree. 548 Private method to resort the tree.
534 """ 549 """
535 self.repositoryList.sortItems( 550 if self.__integratedWidget:
536 self.repositoryList.sortColumn(), 551 self.repositoryList.sortItems(
537 self.repositoryList.header().sortIndicatorOrder(), 552 self.repositoryList.sortColumn(),
538 ) 553 Qt.SortOrder.AscendingOrder,
554 )
555 else:
556 self.repositoryList.sortItems(
557 self.repositoryList.sortColumn(),
558 self.repositoryList.header().sortIndicatorOrder(),
559 )
560
561 def __initHeaderItemsCache(self):
562 """
563 Private method to initialize the cache variables for the header items.
564 """
565 self.__statusItems = defaultdict(lambda: None)
566 self.__categoryItems = defaultdict(dict)
539 567
540 def __populateList(self): 568 def __populateList(self):
541 """ 569 """
542 Private method to populate the list of available plugins. 570 Private method to populate the list of available plugins.
543 """ 571 """
572 self.__initHeaderItemsCache()
544 self.repositoryList.clear() 573 self.repositoryList.clear()
545 self.__stableItem = None
546 self.__unstableItem = None
547 self.__unknownItem = None
548 self.__obsoleteItem = None
549 574
550 self.__newItems = 0 575 self.__newItems = 0
551 self.__updateLocalItems = 0 576 self.__updateLocalItems = 0
552 self.__updateRemoteItems = 0 577 self.__updateRemoteItems = 0
553 578
602 627
603 def __downloadFile(self, url, filename, doneMethod=None): 628 def __downloadFile(self, url, filename, doneMethod=None):
604 """ 629 """
605 Private slot to download the given file. 630 Private slot to download the given file.
606 631
607 @param url URL for the download (string) 632 @param url URL for the download
608 @param filename local name of the file (string) 633 @type str
634 @param filename local name of the file
635 @type str
609 @param doneMethod method to be called when done 636 @param doneMethod method to be called when done
637 @type function
610 """ 638 """
611 if self.__online: 639 if self.__online:
612 self.__updateButton.setEnabled(False) 640 self.__updateButton.setEnabled(False)
613 self.__downloadButton.setEnabled(False) 641 self.__downloadButton.setEnabled(False)
614 self.__downloadInstallButton.setEnabled(False) 642 self.__downloadInstallButton.setEnabled(False)
650 @param reply reference to the reply object of the download 678 @param reply reference to the reply object of the download
651 @type QNetworkReply 679 @type QNetworkReply
652 @param fileName local name of the file 680 @param fileName local name of the file
653 @type str 681 @type str
654 @param doneMethod method to be called when done 682 @param doneMethod method to be called when done
655 @type func 683 @type function
656 """ 684 """
657 self.__updateButton.setEnabled(True) 685 self.__updateButton.setEnabled(True)
658 if not self.__integratedWidget: 686 if not self.__integratedWidget:
659 self.__closeButton.setEnabled(True) 687 self.__closeButton.setEnabled(True)
660 self.__downloadCancelButton.setEnabled(False) 688 self.__downloadCancelButton.setEnabled(False)
718 746
719 def __downloadProgress(self, done, total): 747 def __downloadProgress(self, done, total):
720 """ 748 """
721 Private slot to show the download progress. 749 Private slot to show the download progress.
722 750
723 @param done number of bytes downloaded so far (integer) 751 @param done number of bytes downloaded so far
724 @param total total bytes to be downloaded (integer) 752 @type int
753 @param total total bytes to be downloaded
754 @type int
725 """ 755 """
726 if total: 756 if total:
727 self.downloadProgress.setMaximum(total) 757 self.downloadProgress.setMaximum(total)
728 self.downloadProgress.setValue(done) 758 self.downloadProgress.setValue(done)
729 759
730 def addEntry( 760 def addEntry(
731 self, name, short, description, url, author, version, filename, status 761 self,
762 name,
763 short,
764 description,
765 url,
766 author,
767 version,
768 filename,
769 status,
770 category,
732 ): 771 ):
733 """ 772 """
734 Public method to add an entry to the list. 773 Public method to add an entry to the list.
735 774
736 @param name data for the name field (string) 775 @param name data for the name field
737 @param short data for the short field (string) 776 @type str
738 @param description data for the description field (list of strings) 777 @param short data for the short field
739 @param url data for the url field (string) 778 @type str
740 @param author data for the author field (string) 779 @param description data for the description field
741 @param version data for the version field (string) 780 @type list of str
742 @param filename data for the filename field (string) 781 @param url data for the url field
743 @param status status of the plugin (string [stable, unstable, unknown]) 782 @type str
783 @param author data for the author field
784 @type str
785 @param version data for the version field
786 @type str
787 @param filename data for the filename field
788 @type str
789 @param status status of the plugin (one of stable, unstable, unknown)
790 @type str
791 @param category category designation of the plugin
792 @type str
744 """ 793 """
745 pluginName = filename.rsplit("-", 1)[0] 794 pluginName = filename.rsplit("-", 1)[0]
746 if pluginName in self.__hiddenPlugins: 795 if pluginName in self.__hiddenPlugins:
747 return 796 return
748 797
749 if status == "stable": 798 # 1. determine and create the status item
750 if self.__stableItem is None: 799 statusItem = self.__statusItems[status]
751 self.__stableItem = QTreeWidgetItem( 800 if statusItem is None:
752 self.repositoryList, [self.tr("Stable")] 801 statusItem = QTreeWidgetItem(
753 ) 802 self.repositoryList,
754 self.__stableItem.setExpanded(True) 803 [
755 parent = self.__stableItem 804 self.__statusTranslations.get(
756 elif status == "unstable": 805 status, self.__statusTranslations["unknown"]
757 if self.__unstableItem is None: 806 )
758 self.__unstableItem = QTreeWidgetItem( 807 ],
759 self.repositoryList, [self.tr("Unstable")] 808 )
760 ) 809 statusItem.setExpanded(True)
761 self.__unstableItem.setExpanded(True) 810 statusItem.setFirstColumnSpanned(True)
762 parent = self.__unstableItem 811 self.__statusItems[status] = statusItem
763 elif status == "obsolete": 812
764 if self.__obsoleteItem is None: 813 # 2. determine and create the category item
765 self.__obsoleteItem = QTreeWidgetItem( 814 try:
766 self.repositoryList, [self.tr("Obsolete")] 815 categoryItem = self.__categoryItems[status][category]
767 ) 816 except KeyError:
768 self.__obsoleteItem.setExpanded(True) 817 # create the category item
769 parent = self.__obsoleteItem 818 categoryItem = QTreeWidgetItem(statusItem, [category])
770 else: 819 categoryItem.setExpanded(True)
771 if self.__unknownItem is None: 820 categoryItem.setFirstColumnSpanned(True)
772 self.__unknownItem = QTreeWidgetItem( 821 self.__categoryItems[status][category] = categoryItem
773 self.repositoryList, [self.tr("Unknown")] 822
774 ) 823 # 3. create the plugin item
775 self.__unknownItem.setExpanded(True)
776 parent = self.__unknownItem
777
778 if self.__integratedWidget: 824 if self.__integratedWidget:
779 entryFormat = "<b>{0}</b> - Version: <i>{1}</i><br/>{2}" 825 entryFormat = "<b>{0}</b> - Version: <i>{1}</i><br/>{2}"
780 itm = QTreeWidgetItem(parent) 826 itm = QTreeWidgetItem(categoryItem)
781 itm.setFirstColumnSpanned(True) 827 itm.setFirstColumnSpanned(True)
782 label = QLabel(entryFormat.format(name, version, short)) 828 label = QLabel(entryFormat.format(name, version, short))
783 self.repositoryList.setItemWidget(itm, 0, label) 829 self.repositoryList.setItemWidget(itm, 0, label)
784 else: 830 else:
785 itm = QTreeWidgetItem(parent, [name, version, short]) 831 itm = QTreeWidgetItem(categoryItem, [name, version, short])
786 832
787 itm.setData(0, PluginRepositoryWidget.UrlRole, url) 833 itm.setData(0, PluginRepositoryWidget.UrlRole, url)
788 itm.setData(0, PluginRepositoryWidget.FilenameRole, filename) 834 itm.setData(0, PluginRepositoryWidget.FilenameRole, filename)
789 itm.setData(0, PluginRepositoryWidget.AuthorRole, author) 835 itm.setData(0, PluginRepositoryWidget.AuthorRole, author)
790 itm.setData(0, PluginRepositoryWidget.DescrRole, description) 836 itm.setData(0, PluginRepositoryWidget.DescrRole, description)
791 837
792 iconColumn = 0 if self.__integratedWidget else 1 838 iconColumn = 0 if self.__integratedWidget else 1
793 updateStatus = self.__updateStatus(filename, version) 839 updateStatus = self.__updateStatus(filename, version)
794 if updateStatus == PluginRepositoryWidget.PluginStatusUpToDate: 840 if updateStatus == PluginStatus.UpToDate:
795 itm.setIcon(iconColumn, EricPixmapCache.getIcon("empty")) 841 itm.setIcon(iconColumn, EricPixmapCache.getIcon("empty"))
796 itm.setToolTip(iconColumn, self.tr("up-to-date")) 842 itm.setToolTip(iconColumn, self.tr("up-to-date"))
797 elif updateStatus == PluginRepositoryWidget.PluginStatusNew: 843 elif updateStatus == PluginStatus.New:
798 itm.setIcon(iconColumn, EricPixmapCache.getIcon("download")) 844 itm.setIcon(iconColumn, EricPixmapCache.getIcon("download"))
799 itm.setToolTip(iconColumn, self.tr("new download available")) 845 itm.setToolTip(iconColumn, self.tr("new download available"))
800 self.__newItems += 1 846 self.__newItems += 1
801 elif updateStatus == PluginRepositoryWidget.PluginStatusLocalUpdate: 847 elif updateStatus == PluginStatus.LocalUpdate:
802 itm.setIcon(iconColumn, EricPixmapCache.getIcon("updateLocal")) 848 itm.setIcon(iconColumn, EricPixmapCache.getIcon("updateLocal"))
803 itm.setToolTip(iconColumn, self.tr("update installable")) 849 itm.setToolTip(iconColumn, self.tr("update installable"))
804 self.__updateLocalItems += 1 850 self.__updateLocalItems += 1
805 elif updateStatus == PluginRepositoryWidget.PluginStatusRemoteUpdate: 851 elif updateStatus == PluginStatus.RemoteUpdate:
806 itm.setIcon(iconColumn, EricPixmapCache.getIcon("updateRemote")) 852 itm.setIcon(iconColumn, EricPixmapCache.getIcon("updateRemote"))
807 itm.setToolTip(iconColumn, self.tr("updated download available")) 853 itm.setToolTip(iconColumn, self.tr("updated download available"))
808 self.__updateRemoteItems += 1 854 self.__updateRemoteItems += 1
809 elif updateStatus == PluginRepositoryWidget.PluginStatusError: 855 elif updateStatus == PluginStatus.Error:
810 itm.setIcon(iconColumn, EricPixmapCache.getIcon("warning")) 856 itm.setIcon(iconColumn, EricPixmapCache.getIcon("warning"))
811 itm.setToolTip(iconColumn, self.tr("error determining status")) 857 itm.setToolTip(iconColumn, self.tr("error determining status"))
812 858
813 def __updateStatus(self, filename, version): 859 def __updateStatus(self, filename, version):
814 """ 860 """
815 Private method to check the given archive update status. 861 Private method to check the given archive update status.
816 862
817 @param filename data for the filename field (string) 863 @param filename data for the filename field
818 @param version data for the version field (string) 864 @type str
819 @return plug-in update status (integer, one of PluginStatusNew, 865 @param version data for the version field
820 PluginStatusUpToDate, PluginStatusLocalUpdate, 866 @type str
821 PluginStatusRemoteUpdate) 867 @return plug-in update status
868 @rtype int (one of PluginStatusNew, PluginStatusUpToDate,
869 PluginStatusLocalUpdate, PluginStatusRemoteUpdate)
822 """ 870 """
823 archive = os.path.join(Preferences.getPluginManager("DownloadPath"), filename) 871 archive = os.path.join(Preferences.getPluginManager("DownloadPath"), filename)
824 872
825 # check, if it is an update (i.e. we already have archives 873 # check, if it is an update (i.e. we already have archives
826 # with the same pattern) 874 # with the same pattern)
828 if len(glob.glob(archivesPattern)) == 0: 876 if len(glob.glob(archivesPattern)) == 0:
829 # Check against installed/loaded plug-ins 877 # Check against installed/loaded plug-ins
830 pluginName = filename.rsplit("-", 1)[0] 878 pluginName = filename.rsplit("-", 1)[0]
831 pluginDetails = self.__pluginManager.getPluginDetails(pluginName) 879 pluginDetails = self.__pluginManager.getPluginDetails(pluginName)
832 if pluginDetails is None or pluginDetails["moduleName"] != pluginName: 880 if pluginDetails is None or pluginDetails["moduleName"] != pluginName:
833 return PluginRepositoryWidget.PluginStatusNew 881 return PluginStatus.New
834 if pluginDetails["error"]: 882 if pluginDetails["error"]:
835 return PluginRepositoryWidget.PluginStatusError 883 return PluginStatus.Error
836 pluginVersionTuple = Globals.versionToTuple(pluginDetails["version"])[:3] 884 pluginVersionTuple = Globals.versionToTuple(pluginDetails["version"])[:3]
837 versionTuple = Globals.versionToTuple(version)[:3] 885 versionTuple = Globals.versionToTuple(version)[:3]
838 if pluginVersionTuple < versionTuple: 886 if pluginVersionTuple < versionTuple:
839 return PluginRepositoryWidget.PluginStatusRemoteUpdate 887 return PluginStatus.RemoteUpdate
840 else: 888 else:
841 return PluginRepositoryWidget.PluginStatusUpToDate 889 return PluginStatus.UpToDate
842 890
843 # check, if the archive exists 891 # check, if the archive exists
844 if not os.path.exists(archive): 892 if not os.path.exists(archive):
845 return PluginRepositoryWidget.PluginStatusRemoteUpdate 893 return PluginStatus.RemoteUpdate
846 894
847 # check, if the archive is a valid zip file 895 # check, if the archive is a valid zip file
848 if not zipfile.is_zipfile(archive): 896 if not zipfile.is_zipfile(archive):
849 return PluginRepositoryWidget.PluginStatusRemoteUpdate 897 return PluginStatus.RemoteUpdate
850 898
851 zipFile = zipfile.ZipFile(archive, "r") 899 zipFile = zipfile.ZipFile(archive, "r")
852 try: 900 try:
853 aversion = zipFile.read("VERSION").decode("utf-8") 901 aversion = zipFile.read("VERSION").decode("utf-8")
854 except KeyError: 902 except KeyError:
858 if aversion == version: 906 if aversion == version:
859 # Check against installed/loaded plug-ins 907 # Check against installed/loaded plug-ins
860 pluginName = filename.rsplit("-", 1)[0] 908 pluginName = filename.rsplit("-", 1)[0]
861 pluginDetails = self.__pluginManager.getPluginDetails(pluginName) 909 pluginDetails = self.__pluginManager.getPluginDetails(pluginName)
862 if pluginDetails is None: 910 if pluginDetails is None:
863 return PluginRepositoryWidget.PluginStatusLocalUpdate 911 return PluginStatus.LocalUpdate
864 if ( 912 if (
865 Globals.versionToTuple(pluginDetails["version"])[:3] 913 Globals.versionToTuple(pluginDetails["version"])[:3]
866 < Globals.versionToTuple(version)[:3] 914 < Globals.versionToTuple(version)[:3]
867 ): 915 ):
868 return PluginRepositoryWidget.PluginStatusLocalUpdate 916 return PluginStatus.LocalUpdate
869 else: 917 else:
870 return PluginRepositoryWidget.PluginStatusUpToDate 918 return PluginStatus.UpToDate
871 else: 919 else:
872 return PluginRepositoryWidget.PluginStatusRemoteUpdate 920 return PluginStatus.RemoteUpdate
873 921
874 def __sslErrors(self, reply, errors): 922 def __sslErrors(self, reply, errors):
875 """ 923 """
876 Private slot to handle SSL errors. 924 Private slot to handle SSL errors.
877 925
878 @param reply reference to the reply object (QNetworkReply) 926 @param reply reference to the reply object
879 @param errors list of SSL errors (list of QSslError) 927 @type QNetworkReply
928 @param errors list of SSL errors
929 @type list of QSslError
880 """ 930 """
881 ignored = self.__sslErrorHandler.sslErrorsReply(reply, errors)[0] 931 ignored = self.__sslErrorHandler.sslErrorsReply(reply, errors)[0]
882 if ignored == EricSslErrorState.NOT_IGNORED: 932 if ignored == EricSslErrorState.NOT_IGNORED:
883 self.__downloadCancel(reply) 933 self.__downloadCancel(reply)
884 934
885 def getDownloadedPlugins(self): 935 def getDownloadedPlugins(self):
886 """ 936 """
887 Public method to get the list of recently downloaded plugin files. 937 Public method to get the list of recently downloaded plugin files.
888 938
889 @return list of plugin filenames (list of strings) 939 @return list of plugin filenames
940 @rtype list of str
890 """ 941 """
891 return self.__pluginsDownloaded 942 return self.__pluginsDownloaded
892 943
893 @pyqtSlot(bool) 944 @pyqtSlot(bool)
894 def on_repositoryUrlEditButton_toggled(self, checked): 945 def on_repositoryUrlEditButton_toggled(self, checked):
895 """ 946 """
896 Private slot to set the read only status of the repository URL line 947 Private slot to set the read only status of the repository URL line
897 edit. 948 edit.
898 949
899 @param checked state of the push button (boolean) 950 @param checked state of the push button
951 @type bool
900 """ 952 """
901 self.repositoryUrlEdit.setReadOnly(not checked) 953 self.repositoryUrlEdit.setReadOnly(not checked)
902 954
903 def __closeAndInstall(self): 955 def __closeAndInstall(self):
904 """ 956 """
942 994
943 def __hasHiddenPlugins(self): 995 def __hasHiddenPlugins(self):
944 """ 996 """
945 Private method to check, if there are any hidden plug-ins. 997 Private method to check, if there are any hidden plug-ins.
946 998
947 @return flag indicating the presence of hidden plug-ins (boolean) 999 @return flag indicating the presence of hidden plug-ins
1000 @rtype bool
948 """ 1001 """
949 return bool(self.__hiddenPlugins) 1002 return bool(self.__hiddenPlugins)
950 1003
951 def __updateHiddenPluginsList(self, hideList): 1004 def __updateHiddenPluginsList(self, hideList):
952 """ 1005 """
953 Private method to store the list of hidden plug-ins to the settings. 1006 Private method to store the list of hidden plug-ins to the settings.
954 1007
955 @param hideList list of plug-ins to add to the list of hidden ones 1008 @param hideList list of plug-ins to add to the list of hidden ones
956 (list of string) 1009 @type list of str
957 """ 1010 """
958 if hideList: 1011 if hideList:
959 self.__hiddenPlugins.extend( 1012 self.__hiddenPlugins.extend(
960 [p for p in hideList if p not in self.__hiddenPlugins] 1013 [p for p in hideList if p not in self.__hiddenPlugins]
961 ) 1014 )
1008 1061
1009 def getDownloadedPlugins(self): 1062 def getDownloadedPlugins(self):
1010 """ 1063 """
1011 Public method to get the list of recently downloaded plugin files. 1064 Public method to get the list of recently downloaded plugin files.
1012 1065
1013 @return list of plugin filenames (list of strings) 1066 @return list of plugin filenames
1067 @rtype list of str
1014 """ 1068 """
1015 return self.cw.getDownloadedPlugins() 1069 return self.cw.getDownloadedPlugins()
1016 1070
1017 1071
1018 class PluginRepositoryWindow(EricMainWindow): 1072 class PluginRepositoryWindow(EricMainWindow):
1022 1076
1023 def __init__(self, parent=None): 1077 def __init__(self, parent=None):
1024 """ 1078 """
1025 Constructor 1079 Constructor
1026 1080
1027 @param parent reference to the parent widget (QWidget) 1081 @param parent reference to the parent widget
1082 @type QWidget
1028 """ 1083 """
1029 super().__init__(parent) 1084 super().__init__(parent)
1030 self.cw = PluginRepositoryWidget(None, parent=self) 1085 self.cw = PluginRepositoryWidget(None, parent=self)
1031 size = self.cw.size() 1086 size = self.cw.size()
1032 self.setCentralWidget(self.cw) 1087 self.setCentralWidget(self.cw)
1074 @type bool 1129 @type bool
1075 """ 1130 """
1076 pluginsRegister = [] # list of plug-ins contained in the repository 1131 pluginsRegister = [] # list of plug-ins contained in the repository
1077 1132
1078 def registerPlugin( 1133 def registerPlugin(
1079 name, short, description, url, author, version, filename, status # noqa: U100 1134 name, # noqa: U100
1135 short, # noqa: U100
1136 description, # noqa: U100
1137 url,
1138 author, # noqa: U100
1139 version, # noqa: U100
1140 filename, # noqa: U100
1141 status, # noqa: U100
1142 category, # noqa: U100
1080 ): 1143 ):
1081 """ 1144 """
1082 Method to register a plug-in's data. 1145 Method to register a plug-in's data.
1083 1146
1084 @param name data for the name field (string) 1147 @param name data for the name field
1085 @param short data for the short field (string) 1148 @type str
1086 @param description data for the description field (list of strings) 1149 @param short data for the short field
1087 @param url data for the url field (string) 1150 @type str
1088 @param author data for the author field (string) 1151 @param description data for the description field
1089 @param version data for the version field (string) 1152 @type list of str
1090 @param filename data for the filename field (string) 1153 @param url data for the url field
1091 @param status status of the plugin (string [stable, unstable, unknown]) 1154 @type str
1155 @param author data for the author field
1156 @type str
1157 @param version data for the version field
1158 @type str
1159 @param filename data for the filename field
1160 @type str
1161 @param status status of the plugin (one of stable, unstable, unknown)
1162 @type str
1163 @param category category designation of the plugin
1164 @type str
1092 """ 1165 """
1093 pluginName = os.path.splitext(url.rsplit("/", 1)[1])[0] 1166 pluginName = os.path.splitext(url.rsplit("/", 1)[1])[0]
1094 if pluginName not in pluginsRegister: 1167 if pluginName not in pluginsRegister:
1095 pluginsRegister.append(pluginName) 1168 pluginsRegister.append(pluginName)
1096 1169

eric ide

mercurial