Skip to content

Commit 03a3180

Browse files
authored
Merge 7cbf6dc into b93e08f
2 parents b93e08f + 7cbf6dc commit 03a3180

1 file changed

Lines changed: 33 additions & 4 deletions

File tree

source/addonHandler/__init__.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# See the file COPYING for more details.
66

77
from abc import abstractmethod, ABC
8+
import glob
89
import sys
910
import os.path
1011
import gettext
@@ -19,7 +20,6 @@
1920
from typing import (
2021
Callable,
2122
Dict,
22-
List,
2323
Optional,
2424
Set,
2525
TYPE_CHECKING,
@@ -203,6 +203,22 @@ def cleanupRemovedDisabledAddons(self) -> None:
203203
log.debug(f"Discarding {disabledAddonName} from disabled add-ons as it has been uninstalled.")
204204
self[AddonStateCategory.DISABLED].discard(disabledAddonName)
205205

206+
def _cleanupInstalledAddons(self) -> None:
207+
# There should be no pending installs after add-ons have been loaded during initialization.
208+
for path in _getDefaultAddonPaths():
209+
pendingInstallPaths = glob.glob(f"{path}/*.{ADDON_PENDINGINSTALL_SUFFIX}")
210+
for pendingInstallPath in pendingInstallPaths:
211+
if os.path.exists(pendingInstallPath):
212+
try:
213+
log.error(f"Removing failed install of {pendingInstallPath}")
214+
shutil.rmtree(pendingInstallPath, ignore_errors=True)
215+
except OSError:
216+
log.error(f"Failed to remove {pendingInstallPath}", exc_info=True)
217+
218+
if self[AddonStateCategory.PENDING_INSTALL]:
219+
log.error(f"Discarding {self[AddonStateCategory.PENDING_INSTALL]} from pending install add-ons as their install failed.")
220+
self[AddonStateCategory.PENDING_INSTALL].clear()
221+
206222
def _cleanupCompatibleAddonsFromDowngrade(self) -> None:
207223
from addonStore.dataManager import addonDataManager
208224
installedAddons = addonDataManager._installedAddonsCache.installedAddons
@@ -287,6 +303,7 @@ def initialize():
287303
getAvailableAddons(refresh=True, isFirstLoad=True)
288304
state.cleanupRemovedDisabledAddons()
289305
state._cleanupCompatibleAddonsFromDowngrade()
306+
state._cleanupInstalledAddons()
290307
if NVDAState.shouldWriteToDisk():
291308
state.save()
292309
initializeModulePackagePaths()
@@ -303,8 +320,8 @@ def terminate():
303320
pass
304321

305322

306-
def _getDefaultAddonPaths() -> List[str]:
307-
""" Returns paths where addons can be found.
323+
def _getDefaultAddonPaths() -> list[str]:
324+
r""" Returns paths where addons can be found.
308325
For now, only <userConfig>\addons is supported.
309326
"""
310327
addon_paths = []
@@ -497,14 +514,26 @@ def __init__(self, path: str):
497514
_report_manifest_errors(self.manifest)
498515
raise AddonError("Manifest file has errors.")
499516

500-
def completeInstall(self) -> str:
517+
def completeInstall(self) -> Optional[str]:
518+
if not os.path.exists(self.pendingInstallPath):
519+
log.error(f"Pending install path {self.pendingInstallPath} does not exist")
520+
return None
521+
501522
try:
502523
os.rename(self.pendingInstallPath, self.installPath)
503524
state[AddonStateCategory.PENDING_INSTALL].discard(self.name)
504525
return self.installPath
505526
except OSError:
506527
log.error(f"Failed to complete addon installation for {self.name}", exc_info=True)
507528

529+
# Remove pending install folder
530+
try:
531+
log.error(f"Removing failed install of {self.pendingInstallPath}")
532+
shutil.rmtree(self.pendingInstallPath, ignore_errors=True)
533+
state[AddonStateCategory.PENDING_INSTALL].discard(self.name)
534+
except OSError:
535+
log.error(f"Failed to remove {self.pendingInstallPath}", exc_info=True)
536+
508537
def requestRemove(self):
509538
"""Marks this addon for removal on NVDA restart."""
510539
if self.isPendingInstall and not self.isInstalled:

0 commit comments

Comments
 (0)