From 00dbfb2fde4424bdd102545ee6f1d4bb916e8cf0 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sat, 26 Sep 2026 21:02:37 -0500 Subject: [PATCH] Move constraints resolution off GUI thread Assisted-by: Claude Opus 5.5 --- AddonManager.py | 12 ++++++++++++ AddonManagerTest/app/test_workers_startup.py | 10 ++++++++++ addonmanager_workers_startup.py | 9 +++++++++ 3 files changed, 31 insertions(+) diff --git a/AddonManager.py b/AddonManager.py index a34e2497..6cc56ba6 100644 --- a/AddonManager.py +++ b/AddonManager.py @@ -35,6 +35,7 @@ GetBasicAddonStatsWorker, GetAddonScoreWorker, CheckForMissingDependenciesWorker, + LoadPythonConstraintsWorker, ) from addonmanager_installer_gui import ( AddonInstallerGUI, @@ -108,6 +109,7 @@ class CommandAddonManager(QtCore.QObject): "get_basic_addon_stats_worker", "get_addon_score_worker", "check_missing_dependencies_worker", + "load_python_constraints_worker", ] lock = threading.Lock() @@ -144,6 +146,7 @@ def __init__(self): self.get_addon_score_worker = None self.get_basic_addon_stats_worker = None self.check_missing_dependencies_worker = None + self.load_python_constraints_worker = None self.manage_python_packages_dialog = None self.missing_dependency_installer = None @@ -350,6 +353,7 @@ def startup(self) -> None: # self.do_next_startup_phase if it is not launching a worker self.startup_sequence = [ self.populate_packages_table, + self.load_python_constraints, self.activate_table_widgets, self.check_updates, self.check_missing_dependencies, @@ -385,6 +389,14 @@ def populate_packages_table(self) -> None: self.create_addon_list_worker.progress_made.connect(self.update_progress_bar) self.create_addon_list_worker.start() + def load_python_constraints(self) -> None: + self.load_python_constraints_worker = LoadPythonConstraintsWorker() + self.update_progress_bar( + translate("AddonsInstaller", "Loading Python package constraints"), 0, 100 + ) + self.load_python_constraints_worker.finished.connect(self.do_next_startup_phase) + self.load_python_constraints_worker.start() + def activate_table_widgets(self) -> None: self.composite_view.package_list.setEnabled(True) self.composite_view.package_list.ui.view_bar.search.setFocus() diff --git a/AddonManagerTest/app/test_workers_startup.py b/AddonManagerTest/app/test_workers_startup.py index bb180207..7b9eed2b 100644 --- a/AddonManagerTest/app/test_workers_startup.py +++ b/AddonManagerTest/app/test_workers_startup.py @@ -775,3 +775,13 @@ def test_missing_icon_is_not_fatal(self, mock_network_manager, _): self.assertEqual("My Custom Addon", addon.display_name) self.assertFalse(addon.icon_data) + + +class TestLoadPythonConstraintsWorker(unittest.TestCase): + + @patch("addonmanager_workers_startup.get_constraints") + def test_run_reloads_the_shared_constraints(self, mock_get_constraints): + """Running the worker fetches the constraints, so later GUI-thread users find them loaded.""" + addonmanager_workers_startup.LoadPythonConstraintsWorker().run() + + mock_get_constraints.return_value.reload.assert_called_once_with() diff --git a/addonmanager_workers_startup.py b/addonmanager_workers_startup.py index b312c4a3..25d41af4 100644 --- a/addonmanager_workers_startup.py +++ b/addonmanager_workers_startup.py @@ -51,6 +51,7 @@ get_branch_from_metadata, get_icon_from_metadata, ) +from addonmanager_python_constraints import get_constraints import addonmanager_utilities as utils import addonmanager_freecad_interface as fci @@ -949,3 +950,11 @@ def run(self): else: fci.Console.PrintLog(message) fci.Console.PrintLog(details) + + +class LoadPythonConstraintsWorker(QtCore.QThread): + """A worker class to fetch the vetted Python package constraints. This uses a blocking network + fetch, so cannot run on the main GUI thread.""" + + def run(self): + get_constraints().reload()