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]]: