diff --git a/src/Mod/AddonManager/AddonManager.py b/src/Mod/AddonManager/AddonManager.py index 882c3b1eedca..dc37437777de 100644 --- a/src/Mod/AddonManager/AddonManager.py +++ b/src/Mod/AddonManager/AddonManager.py @@ -42,7 +42,7 @@ from package_details import PackageDetails from AddonManagerRepo import AddonManagerRepo -from NetworkManager import HAVE_QTNETWORK +from NetworkManager import HAVE_QTNETWORK, InitializeNetworkManager __title__ = "FreeCAD Addon Manager Module" __author__ = "Yorik van Havre", "Jonathan Wiedemann", "Kurt Kremitzki", "Chris Hennes" @@ -131,6 +131,8 @@ def GetResources(self) -> Dict[str, str]: def Activated(self) -> None: + InitializeNetworkManager() + # display first use dialog if needed pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") readWarning = pref.GetBool("readWarning2022", False) diff --git a/src/Mod/AddonManager/NetworkManager.py b/src/Mod/AddonManager/NetworkManager.py index 5c9314bc1c35..b69089d87d59 100644 --- a/src/Mod/AddonManager/NetworkManager.py +++ b/src/Mod/AddonManager/NetworkManager.py @@ -146,6 +146,9 @@ def __init__(self): self.synchronous_complete: Dict[int, bool] = {} self.synchronous_result_data: Dict[int, QtCore.QByteArray] = {} + # Make sure we exit nicely on quit + QtCore.QCoreApplication.instance().aboutToQuit.connect(self.aboutToQuit) + def run(self): """Do not call directly: use start() to begin the event loop on a new thread.""" @@ -257,6 +260,10 @@ def run(self): pass QtCore.QCoreApplication.processEvents() + def aboutToQuit(self): + self.requestInterruption() + + def submit_unmonitored_get(self, url: str) -> int: """Adds this request to the queue, and returns an index that can be used by calling code in conjunction with the completed() signal to handle the results of the call. All data is @@ -506,14 +513,18 @@ def abort(self, _): pass # Nothing to do -AM_NETWORK_MANAGER = NetworkManager() -AM_NETWORK_MANAGER.start() - +def InitializeNetworkManager(): + global AM_NETWORK_MANAGER + if AM_NETWORK_MANAGER is None: + AM_NETWORK_MANAGER = NetworkManager() + AM_NETWORK_MANAGER.start() if __name__ == "__main__": app = QtCore.QCoreApplication() + InitializeNetworkManager() + count = 0 # For testing, create several network requests and send them off in quick succession: diff --git a/src/Mod/AddonManager/addonmanager_macro.py b/src/Mod/AddonManager/addonmanager_macro.py index 4dfd1f867493..84e774459e39 100644 --- a/src/Mod/AddonManager/addonmanager_macro.py +++ b/src/Mod/AddonManager/addonmanager_macro.py @@ -30,7 +30,7 @@ from typing import Dict, Tuple, List, Union import FreeCAD -from NetworkManager import AM_NETWORK_MANAGER +import NetworkManager translate = FreeCAD.Qt.translate @@ -166,7 +166,7 @@ def fill_details_from_code(self, code: str) -> None: def fill_details_from_wiki(self, url): code = "" - p = AM_NETWORK_MANAGER.blocking_get(url) + p = NetworkManager.AM_NETWORK_MANAGER.blocking_get(url) if not p: FreeCAD.Console.PrintWarning( translate( @@ -183,7 +183,7 @@ def fill_details_from_wiki(self, url): rawcodeurl = re.findall('rawcodeurl.*?href="(http.*?)">', p) if rawcodeurl: rawcodeurl = rawcodeurl[0] - u2 = AM_NETWORK_MANAGER.blocking_get(rawcodeurl) + u2 = NetworkManager.AM_NETWORK_MANAGER.blocking_get(rawcodeurl) if not u2: FreeCAD.Console.PrintWarning( translate( diff --git a/src/Mod/AddonManager/addonmanager_workers.py b/src/Mod/AddonManager/addonmanager_workers.py index b0d1a576dfc8..a10314a05b23 100644 --- a/src/Mod/AddonManager/addonmanager_workers.py +++ b/src/Mod/AddonManager/addonmanager_workers.py @@ -52,7 +52,7 @@ from addonmanager_macro import Macro from AddonManagerRepo import AddonManagerRepo -from NetworkManager import AM_NETWORK_MANAGER +import NetworkManager translate = FreeCAD.Qt.translate @@ -119,7 +119,7 @@ def run(self): translate("AddonsInstaller", "Checking network connection...\n") ) url = "https://api.github.com/zen" - result = AM_NETWORK_MANAGER.blocking_get(url) + result = NetworkManager.AM_NETWORK_MANAGER.blocking_get(url) if QtCore.QThread.currentThread().isInterruptionRequested(): return if not result: @@ -153,7 +153,7 @@ def run(self): # update info lists global obsolete, macros_reject_list, mod_reject_list, py2only - p = AM_NETWORK_MANAGER.blocking_get( + p = NetworkManager.AM_NETWORK_MANAGER.blocking_get( "https://raw.githubusercontent.com/FreeCAD/FreeCAD-addons/master/addonflags.json" ) if p: @@ -254,7 +254,7 @@ def run(self): self.addon_repo.emit(repo) # querying official addons - p = AM_NETWORK_MANAGER.blocking_get( + p = NetworkManager.AM_NETWORK_MANAGER.blocking_get( "https://raw.githubusercontent.com/FreeCAD/FreeCAD-addons/master/.gitmodules" ) if not p: @@ -409,8 +409,8 @@ def check_workbench(self, wb): "AddonManager: " + translate( "AddonsInstaller", - f"Unable to fetch git updates for workbench {wb.name}", - ) + "Unable to fetch git updates for workbench {}", + ).format(wb.name) ) else: try: @@ -425,7 +425,7 @@ def check_workbench(self, wb): self.update_status.emit(wb) except Exception: FreeCAD.Console.PrintWarning( - translate("AddonsInstaller", "git fetch failed for {wb.name}") + translate("AddonsInstaller", "git fetch failed for {}").format(wb.name) ) def check_package(self, package: AddonManagerRepo) -> None: @@ -627,7 +627,7 @@ def retrieve_macros_from_wiki(self): Reads only the page https://wiki.freecad.org/Macros_recipes """ - p = AM_NETWORK_MANAGER.blocking_get("https://wiki.freecad.org/Macros_recipes") + p = NetworkManager.AM_NETWORK_MANAGER.blocking_get("https://wiki.freecad.org/Macros_recipes") if not p: FreeCAD.Console.PrintWarning( translate( @@ -817,7 +817,7 @@ def run(self): readmeurl = utils.get_readme_html_url(self.repo) if not readmeurl: FreeCAD.Console.PrintLog(f"README not found for {url}\n") - p = AM_NETWORK_MANAGER.blocking_get(readmeurl) + p = NetworkManager.AM_NETWORK_MANAGER.blocking_get(readmeurl) if not p: FreeCAD.Console.PrintLog(f"Debug: README not found at {readmeurl}\n") p = p.data().decode("utf8") @@ -831,7 +831,7 @@ def run(self): readmeurl = utils.get_readme_url(self.repo) if not readmeurl: FreeCAD.Console.PrintLog(f"Debug: README not found for {url}\n") - p = AM_NETWORK_MANAGER.blocking_get(readmeurl) + p = NetworkManager.AM_NETWORK_MANAGER.blocking_get(readmeurl) if p: p = p.data().decode("utf8") desc = utils.fix_relative_links(p, readmeurl.rsplit("/README.md")[0]) @@ -853,7 +853,7 @@ def run(self): FreeCAD.Console.PrintLog("Debug: README not found at {readmeurl}\n") if desc == "": # fall back to the description text - p = AM_NETWORK_MANAGER.blocking_get(url) + p = NetworkManager.AM_NETWORK_MANAGER.blocking_get(url) if not p: return p = p.data().decode("utf8") @@ -942,7 +942,7 @@ def loadImages(self, message, url, wbName): storename = os.path.join(store, wbName + name[-remainChars:]) if not os.path.exists(storename): try: - imagedata = AM_NETWORK_MANAGER.blocking_get(path) + imagedata = NetworkManager.AM_NETWORK_MANAGER.blocking_get(path) if not imagedata: raise Exception except Exception: @@ -1225,9 +1225,9 @@ def launch_zip(self, zipdir: str) -> None: self.zipdir = zipdir self.bakdir = bakdir - AM_NETWORK_MANAGER.progress_made.connect(self.update_zip_status) - AM_NETWORK_MANAGER.progress_complete.connect(self.finish_zip) - self.zip_download_index = AM_NETWORK_MANAGER.submit_monitored_get(zipurl) + NetworkManager.AM_NETWORK_MANAGER.progress_made.connect(self.update_zip_status) + NetworkManager.AM_NETWORK_MANAGER.progress_complete.connect(self.finish_zip) + self.zip_download_index = NetworkManager.AM_NETWORK_MANAGER.submit_monitored_get(zipurl) def update_zip_status(self, index: int, bytes_read: int, data_size: int): if index == self.zip_download_index: @@ -1492,7 +1492,7 @@ def __init__(self, repos): self.requests: Dict[ int, (AddonManagerRepo, UpdateMetadataCacheWorker.RequestType) ] = {} - AM_NETWORK_MANAGER.completed.connect(self.download_completed) + NetworkManager.AM_NETWORK_MANAGER.completed.connect(self.download_completed) self.requests_completed = 0 self.total_requests = 0 self.store = os.path.join( @@ -1506,7 +1506,7 @@ def run(self): for repo in self.repos: if repo.url and utils.recognized_git_location(repo): # package.xml - index = AM_NETWORK_MANAGER.submit_unmonitored_get( + index = NetworkManager.AM_NETWORK_MANAGER.submit_unmonitored_get( utils.construct_git_url(repo, "package.xml") ) self.requests[index] = ( @@ -1516,7 +1516,7 @@ def run(self): self.total_requests += 1 # metadata.txt - index = AM_NETWORK_MANAGER.submit_unmonitored_get( + index = NetworkManager.AM_NETWORK_MANAGER.submit_unmonitored_get( utils.construct_git_url(repo, "metadata.txt") ) self.requests[index] = ( @@ -1526,7 +1526,7 @@ def run(self): self.total_requests += 1 # requirements.txt - index = AM_NETWORK_MANAGER.submit_unmonitored_get( + index = NetworkManager.AM_NETWORK_MANAGER.submit_unmonitored_get( utils.construct_git_url(repo, "requirements.txt") ) self.requests[index] = ( @@ -1537,9 +1537,9 @@ def run(self): while self.requests: if current_thread.isInterruptionRequested(): - AM_NETWORK_MANAGER.completed.disconnect(self.download_completed) + NetworkManager.AM_NETWORK_MANAGER.completed.disconnect(self.download_completed) for request in self.requests.keys(): - AM_NETWORK_MANAGER.abort(request) + NetworkManager.AM_NETWORK_MANAGER.abort(request) return # 50 ms maximum between checks for interruption QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 50) @@ -1603,7 +1603,7 @@ def process_package_xml(self, repo: AddonManagerRepo, data: QtCore.QByteArray): icon = repo.Icon icon_url = utils.construct_git_url(repo, icon) - index = AM_NETWORK_MANAGER.submit_unmonitored_get(icon_url) + index = NetworkManager.AM_NETWORK_MANAGER.submit_unmonitored_get(icon_url) self.requests[index] = (repo, UpdateMetadataCacheWorker.RequestType.ICON) self.total_requests += 1