Browse Source

Defensive programming

Long API calls might return after the Local or Remote
PackageList has been deconstructed. Somehow setting
the ownership in QML doesn't seem to work for this.
So we guard against this with a try catch block.

Contributes to: CURA-8587
Jelle Spijker 3 years ago
parent
commit
453de95d12
2 changed files with 18 additions and 7 deletions
  1. 13 7
      plugins/Marketplace/LocalPackageList.py
  2. 5 0
      plugins/Marketplace/PackageList.py

+ 13 - 7
plugins/Marketplace/LocalPackageList.py

@@ -95,10 +95,16 @@ class LocalPackageList(PackageList):
         if len(response_data["data"]) == 0:
             return
 
-        for package_data in response_data["data"]:
-            package = self.getPackageModel(package_data["package_id"])
-            package.download_url = package_data.get("download_url", "")
-            package.can_update = True
-
-        self.sort(attrgetter("sectionTitle", "can_update", "displayName"), key = "package", reverse = True)
-        self._ongoing_requests["check_updates"] = None
+        try:
+            for package_data in response_data["data"]:
+                package = self.getPackageModel(package_data["package_id"])
+                package.download_url = package_data.get("download_url", "")
+                package.can_update = True
+
+            self.sort(attrgetter("sectionTitle", "can_update", "displayName"), key = "package", reverse = True)
+            self._ongoing_requests["check_updates"] = None
+        except RuntimeError:
+            # Setting the ownership of this object to not qml can still result in a RuntimeError. Which can occur when quickly toggling
+            # between de-/constructing RemotePackageLists. This try-except is here to prevent a hard crash when the wrapped C++ object
+            # was deleted when it was still parsing the response
+            return

+ 5 - 0
plugins/Marketplace/PackageList.py

@@ -218,6 +218,11 @@ class PackageList(ListModel):
             Logger.error(f"Failed to write downloaded package to temp file {e}")
             temp_file.close()
             self._downloadError(package_id, update)
+        except RuntimeError:
+            # Setting the ownership of this object to not qml can still result in a RuntimeError. Which can occur when quickly toggling
+            # between de-/constructing Remote or Local PackageLists. This try-except is here to prevent a hard crash when the wrapped C++ object
+            # was deleted when it was still parsing the response
+            return
 
     def _downloadError(self, package_id: str, update: bool = False, reply: Optional["QNetworkReply"] = None, error: Optional["QNetworkReply.NetworkError"] = None) -> None:
         if reply: