From 9e1ed4a68ad43309c2b38f7f5efbb1feac4323cb Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 21 Sep 2022 14:44:35 -0500 Subject: [PATCH] Addon Manager: Show package dependencies --- src/Mod/AddonManager/AddonManager.py | 7 ++-- src/Mod/AddonManager/AddonManager.ui | 2 +- .../PythonDependencyUpdateDialog.ui | 20 ++++++++++- .../manage_python_dependencies.py | 35 ++++++++++++++++++- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/Mod/AddonManager/AddonManager.py b/src/Mod/AddonManager/AddonManager.py index 9159a0ba20..e68e95a2b9 100644 --- a/src/Mod/AddonManager/AddonManager.py +++ b/src/Mod/AddonManager/AddonManager.py @@ -46,14 +46,12 @@ from addonmanager_workers_startup import ( LoadMacrosFromCacheWorker, CheckWorkbenchesForUpdatesWorker, CacheMacroCodeWorker, - GetMacroDetailsWorker, ) from addonmanager_workers_installation import ( InstallWorkbenchWorker, DependencyInstallationWorker, UpdateMetadataCacheWorker, UpdateAllWorker, - UpdateSingleWorker, ) from addonmanager_workers_utility import ConnectionChecker import addonmanager_utilities as utils @@ -66,7 +64,6 @@ from install_to_toolbar import ( remove_custom_toolbar_button, ) from manage_python_dependencies import ( - check_for_python_package_updates, CheckForPythonPackageUpdatesWorker, PythonPackageManager, ) @@ -377,7 +374,7 @@ class CommandAddonManager: self.dialog.buttonDevTools.hide() # Only shown if there are available Python package updates - self.dialog.buttonUpdateDependencies.hide() + #self.dialog.buttonUpdateDependencies.hide() # connect slots self.dialog.rejected.connect(self.reject) @@ -937,7 +934,7 @@ class CommandAddonManager: def show_python_updates_dialog(self) -> None: if not hasattr(self, "manage_python_packages_dialog"): - self.manage_python_packages_dialog = PythonPackageManager() + self.manage_python_packages_dialog = PythonPackageManager(self.item_model.repos) self.manage_python_packages_dialog.show() def show_developer_tools(self) -> None: diff --git a/src/Mod/AddonManager/AddonManager.ui b/src/Mod/AddonManager/AddonManager.ui index b645aa6426..14db928cc2 100644 --- a/src/Mod/AddonManager/AddonManager.ui +++ b/src/Mod/AddonManager/AddonManager.ui @@ -130,7 +130,7 @@ View and update Python package dependencies - Update dependencies + Python dependencies... diff --git a/src/Mod/AddonManager/PythonDependencyUpdateDialog.ui b/src/Mod/AddonManager/PythonDependencyUpdateDialog.ui index 0bb719bf19..af32fadc5e 100644 --- a/src/Mod/AddonManager/PythonDependencyUpdateDialog.ui +++ b/src/Mod/AddonManager/PythonDependencyUpdateDialog.ui @@ -46,8 +46,11 @@ true - 4 + 5 + + true + false @@ -66,6 +69,11 @@ Available version + + + Used by + + @@ -73,6 +81,16 @@ + + + + An asterisk (*) in the "Used by" column indicates an optional dependency. Note that Used by only records direct imports in the Addon. Other Python packages that those packages depend upon may have been installed as well. + + + true + + + diff --git a/src/Mod/AddonManager/manage_python_dependencies.py b/src/Mod/AddonManager/manage_python_dependencies.py index 0d8d61e8c0..6dabba8a7a 100644 --- a/src/Mod/AddonManager/manage_python_dependencies.py +++ b/src/Mod/AddonManager/manage_python_dependencies.py @@ -86,11 +86,21 @@ def call_pip(args) -> List[str]: stderr=subprocess.PIPE, shell=True, check=True, + timeout=30, ) if proc.returncode != 0: pip_failed = True except subprocess.CalledProcessError: pip_failed = True + except subprocess.TimeoutExpired: + FreeCAD.Console.PrintWarning( + translate( + "AddonsInstaller", + "pip took longer than 30 seconds to return results, giving up on it", + ) + ) + FreeCAD.Console.PrintLog(" ".join(call_args)) + pip_failed = True else: pip_failed = True @@ -107,13 +117,14 @@ class PythonPackageManager: """A GUI-based pip interface allowing packages to be updated, either individually or all at once.""" - def __init__(self): + def __init__(self, addons): self.dlg = FreeCADGui.PySideUic.loadUi( os.path.join(os.path.dirname(__file__), "PythonDependencyUpdateDialog.ui") ) self.vendor_path = os.path.join( FreeCAD.getUserAppDataDir(), "AdditionalPythonPackages" ) + self.addons = addons def show(self): """Run the modal dialog""" @@ -143,6 +154,13 @@ class PythonPackageManager: update_counter = 0 self.dlg.tableWidget.setSortingEnabled(False) for package_name, package_details in package_list.items(): + dependent_addons = self._get_dependent_addons(package_name) + dependencies = [] + for addon in dependent_addons: + if addon["optional"]: + dependencies.append(addon['name'] + "*") + else: + dependencies.append(addon['name']) self.dlg.tableWidget.setItem( counter, 0, QtWidgets.QTableWidgetItem(package_name) ) @@ -156,6 +174,11 @@ class PythonPackageManager: 2, QtWidgets.QTableWidgetItem(package_details["available_version"]), ) + self.dlg.tableWidget.setItem( + counter, + 3, + QtWidgets.QTableWidgetItem(", ".join(dependencies)), + ) if len(package_details["available_version"]) > 0: updateButtons.append( QtWidgets.QPushButton(translate("AddonsInstaller", "Update")) @@ -190,6 +213,16 @@ class PythonPackageManager: else: self.dlg.buttonUpdateAll.setEnabled(False) + def _get_dependent_addons(self, package): + dependent_addons = [] + for addon in self.addons: + #if addon.installed_version is not None: + if package.lower() in addon.python_requires: + dependent_addons.append({"name":addon.name,"optional":False}) + elif package.lower() in addon.python_optional: + dependent_addons.append({"name":addon.name,"optional":True}) + return dependent_addons + def _parse_pip_list_output( self, all_packages, outdated_packages ) -> Dict[str, Dict[str, str]]: