From 27349d892d570531d281cd4f996da2e0d1fe6d7d Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Mon, 10 Dec 2012 10:37:29 +1000 Subject: [PATCH] addonHandler: don't make NVDA fail on restart if an addon cannot be removed because its directory is currently in use by another application. Fixes #2860. Specifically: * Addon.completeRemove: always rename the directory to a temp directory with a delete sufix before trying to actually remove the directory. If the rename does not work, then there's nothing we can do for now. Just raise an error. * CompletePendingAddonRemovals: When walking through the set of add-ons, copy the list so that the set can be mutated while walking. Catch an error from Addon.completeRemove and just skip over it. Remove the addonName from the pendingRemoves set for each addon, as long as no error occured when calling Addon.completeRemove. This keeps addons that failed removal and couldn't be renamed for deletion in the pendingRemove state meaning that the GUI will reflect this, and on a subsiquent restart it will try to be removed again. --- source/addonHandler.py | 17 ++++++++++++----- user_docs/en/changes.t2t | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/source/addonHandler.py b/source/addonHandler.py index 860f430288d..728f34c12a8 100644 --- a/source/addonHandler.py +++ b/source/addonHandler.py @@ -65,12 +65,16 @@ def completePendingAddonRemoves(): """Removes any addons that could not be removed on the last run of NVDA""" user_addons = os.path.abspath(os.path.join(globalVars.appArgs.configPath, "addons")) pendingRemovesSet=state['pendingRemovesSet'] - for addonName in pendingRemovesSet: + for addonName in list(pendingRemovesSet): addonPath=os.path.join(user_addons,addonName) if os.path.isdir(addonPath): addon=Addon(addonPath) - addon.completeRemove() - pendingRemovesSet.clear() + try: + addon.completeRemove() + except RuntimeError: + log.exception("Failed to remove %s add-on"%addonName) + continue + pendingRemovesSet.discard(addonName) def completePendingAddonInstalls(): user_addons = os.path.abspath(os.path.join(globalVars.appArgs.configPath, "addons")) @@ -225,11 +229,14 @@ def completeRemove(self,runUninstallTask=True): log.error("task 'onUninstall' on addon '%s' failed"%self.name,exc_info=True) finally: del _availableAddons[self.path] + tempPath=tempfile.mktemp(suffix=DELETEDIR_SUFFIX,dir=os.path.dirname(self.path)) + try: + os.rename(self.path,tempPath) + except (WindowsError,IOError): + raise RuntimeError("Cannot rename add-on path for deletion") shutil.rmtree(self.path,ignore_errors=True) if os.path.exists(self.path): log.error("Error removing addon directory %s, deferring until next NVDA restart"%self.path) - tempPath=tempfile.mktemp(suffix=DELETEDIR_SUFFIX,dir=os.path.dirname(self.path)) - os.rename(self.path,tempPath) @property def name(self): diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t index 5f9df8ac609..2b00fb50765 100644 --- a/user_docs/en/changes.t2t +++ b/user_docs/en/changes.t2t @@ -27,6 +27,7 @@ - The candidate list in the Chinese Simplified Microsoft Pinyin input method under Windows 7 is now correctly read when changing pages with left and right arrow, and when first opening it with Home. - When custom symbol pronunciation information is saved, the advanced "preserve" field is no longer removed. (#2852) - When disabling automatic checking for updates, NVDA no longer has to be restarted in order for the change to fully take effect. +- NVDA no longer fails to start if an add-on cannot be removed due to its directory currently being in use by another application. (#2860) == Changes for Developers ==