-
-
Notifications
You must be signed in to change notification settings - Fork 652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove add-on modules imported in install tasks to avoid conflicts between add-ons #15967
Conversation
@LeonarddeR Would you be able to verify if the changes to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @lukaszgo1 - should I wait until testing from @LeonarddeR before merging?
I've pinged them just because I don't know anyone else who is using |
I don't think this code will introduce regressions for my use cases. |
This way of importing is new to me. Unless I missed something it can work only if you've imported at least one module from your add-on using |
This is correct indeed. |
I guess something like the following should suffice:
|
…tween add-ons (nvaccess#15967) Fixes regression from PR nvaccess#14481. Discovered due to intermittent failures occurring when installing add-ons, when working on nvaccess#15852 and nvaccess#15937 Summary of the issue: When installing and uninstalling add-ons bundling install tasks, installation either fails or behaves unexpectedly in the following scenarios: 1: User installs an add-on User removes it and restarts NVDA User tries to install the same add-on In this scenario when installing the install tasks included in the version of add-on which was just removed are used, which is unexpected. Note that this worked well before nvaccess#14481, not sure why pkgutil.ImpImporter behaves better (I haven't investigated, as it is deprecated). 2: User installs an add-on, which imports its sub module in install tasks using loadModule and also imports a sub module in the same way inside global plugin / app module during normal operation User upgrades the add-on to a different version In this scenario after upgrade the add-on module from the uninstalled version is used during normal operation of NVDA. 3: User installs an add-on which imports one of its modules in install tasks by manipulating sys.path, for real life examples see Instant Translate and Mouse Wheel Scrolling User uninstalls this add-on, restarts NVDA, and tries to install the new version In this scenario installation fails since the imported module from the old version is used. A slight variation of this scenario is to try to install both Instant Translate and Mouse Wheel Scrolling the add-on installed last would fail to be installed, since they both bundle module named donate_dialog but with different functions Description of user facing changes Add-ons are successfully installed in all scenarios described above. Description of development approach All these problems exist because when importing modules in Python if module with the same name exists in sys.modules it is just used without importing it again. To ensure modules are always re-imported when needed, the following has been done: When importing modules we no longer use add-on name to create a name space package in which the module would be imported, the last segment of the add-on path is used instead. This ensures that the package for add-on which is installed would just be its name, and for add-on which is pending install it would end with the pending install suffix. This fixes issue 1. loadModule memorizes all modules it imported for a given add-on. After installation / removal is done they're removed from sys.modules. This fixes issue 2. To remove cached modules imported with the standard import command, we store names of all modules imported before installation / removal, perform it, create list of newly imported modules and remove all newly added modules which belong to the add-on from the cache. This fixes issue 3.
…tween add-ons (nvaccess#15967) Fixes regression from PR nvaccess#14481. Discovered due to intermittent failures occurring when installing add-ons, when working on nvaccess#15852 and nvaccess#15937 Summary of the issue: When installing and uninstalling add-ons bundling install tasks, installation either fails or behaves unexpectedly in the following scenarios: 1: User installs an add-on User removes it and restarts NVDA User tries to install the same add-on In this scenario when installing the install tasks included in the version of add-on which was just removed are used, which is unexpected. Note that this worked well before nvaccess#14481, not sure why pkgutil.ImpImporter behaves better (I haven't investigated, as it is deprecated). 2: User installs an add-on, which imports its sub module in install tasks using loadModule and also imports a sub module in the same way inside global plugin / app module during normal operation User upgrades the add-on to a different version In this scenario after upgrade the add-on module from the uninstalled version is used during normal operation of NVDA. 3: User installs an add-on which imports one of its modules in install tasks by manipulating sys.path, for real life examples see Instant Translate and Mouse Wheel Scrolling User uninstalls this add-on, restarts NVDA, and tries to install the new version In this scenario installation fails since the imported module from the old version is used. A slight variation of this scenario is to try to install both Instant Translate and Mouse Wheel Scrolling the add-on installed last would fail to be installed, since they both bundle module named donate_dialog but with different functions Description of user facing changes Add-ons are successfully installed in all scenarios described above. Description of development approach All these problems exist because when importing modules in Python if module with the same name exists in sys.modules it is just used without importing it again. To ensure modules are always re-imported when needed, the following has been done: When importing modules we no longer use add-on name to create a name space package in which the module would be imported, the last segment of the add-on path is used instead. This ensures that the package for add-on which is installed would just be its name, and for add-on which is pending install it would end with the pending install suffix. This fixes issue 1. loadModule memorizes all modules it imported for a given add-on. After installation / removal is done they're removed from sys.modules. This fixes issue 2. To remove cached modules imported with the standard import command, we store names of all modules imported before installation / removal, perform it, create list of newly imported modules and remove all newly added modules which belong to the add-on from the cache. This fixes issue 3.
…tween add-ons (nvaccess#15967) Fixes regression from PR nvaccess#14481. Discovered due to intermittent failures occurring when installing add-ons, when working on nvaccess#15852 and nvaccess#15937 Summary of the issue: When installing and uninstalling add-ons bundling install tasks, installation either fails or behaves unexpectedly in the following scenarios: 1: User installs an add-on User removes it and restarts NVDA User tries to install the same add-on In this scenario when installing the install tasks included in the version of add-on which was just removed are used, which is unexpected. Note that this worked well before nvaccess#14481, not sure why pkgutil.ImpImporter behaves better (I haven't investigated, as it is deprecated). 2: User installs an add-on, which imports its sub module in install tasks using loadModule and also imports a sub module in the same way inside global plugin / app module during normal operation User upgrades the add-on to a different version In this scenario after upgrade the add-on module from the uninstalled version is used during normal operation of NVDA. 3: User installs an add-on which imports one of its modules in install tasks by manipulating sys.path, for real life examples see Instant Translate and Mouse Wheel Scrolling User uninstalls this add-on, restarts NVDA, and tries to install the new version In this scenario installation fails since the imported module from the old version is used. A slight variation of this scenario is to try to install both Instant Translate and Mouse Wheel Scrolling the add-on installed last would fail to be installed, since they both bundle module named donate_dialog but with different functions Description of user facing changes Add-ons are successfully installed in all scenarios described above. Description of development approach All these problems exist because when importing modules in Python if module with the same name exists in sys.modules it is just used without importing it again. To ensure modules are always re-imported when needed, the following has been done: When importing modules we no longer use add-on name to create a name space package in which the module would be imported, the last segment of the add-on path is used instead. This ensures that the package for add-on which is installed would just be its name, and for add-on which is pending install it would end with the pending install suffix. This fixes issue 1. loadModule memorizes all modules it imported for a given add-on. After installation / removal is done they're removed from sys.modules. This fixes issue 2. To remove cached modules imported with the standard import command, we store names of all modules imported before installation / removal, perform it, create list of newly imported modules and remove all newly added modules which belong to the add-on from the cache. This fixes issue 3.
Fixes #16704 Fixup of #15967 Summary of the issue: In the following code block: release-2024.2/source/addonHandler/__init__.py#L432-L454 If an error is raised from running addon.runInstallTask("onInstall"), it is caught, and then re-raised as AddonError However, this exception is caught again by the finally statement returning the add-on returning from finally suppresses the exception. This means the callers don't correctly handle the expected exception, as it is suppressed, where it should instead prevent the installation from continuing. We have to return the add-on to perform clean up tasks, but we have to know exceptions were raised to cancel the add-on installation Description of user facing changes Add-on installation failures should fail gracefully Description of development approach Instead of raising exceptions, store them and exit the installation process when a failure occurs. If exceptions are stored, cancel the installation and perform the require clean-up tasks
Link to issue number:
Fixes regression from PR #14481.
Discovered due to intermittent failures occurring when installing add-ons, when working on #15852 and #15937
Summary of the issue:
When installing and uninstalling add-ons bundling install tasks, installation either fails or behaves unexpectedly in the following scenarios:
1:
In this scenario when installing the install tasks included in the version of add-on which was just removed are used, which is unexpected. Note that this worked well before #14481, not sure why
pkgutil.ImpImporter
behaves better (I haven't investigated, as it is deprecated).2:
loadModule
and also imports a sub module in the same way inside global plugin / app module during normal operationIn this scenario after upgrade the add-on module from the uninstalled version is used during normal operation of NVDA.
3:
sys.path
, for real life examples see Instant Translate and Mouse Wheel ScrollingIn this scenario installation fails since the imported module from the old version is used. A slight variation of this scenario is to try to install both Instant Translate and Mouse Wheel Scrolling the add-on installed last would fail to be installed, since they both bundle module named
donate_dialog
but with different functionsDescription of user facing changes
Add-ons are successfully installed in all scenarios described above.
Description of development approach
All these problems exist because when importing modules in Python if module with the same name exists in
sys.modules
it is just used without importing it again. To ensure modules are always re-imported when needed, the following has been done:loadModule
memorizes all modules it imported for a given add-on. After installation / removal is done they're removed fromsys.modules
. This fixes issue 2.import
command, we store names of all modules imported before installation / removal, perform it, create list of newly imported modules and remove all newly added modules which belong to the add-on from the cache. This fixes issue 3.Testing strategy:
Executed scenarios from above. Inspected
sys.modules
afterwards to make sure no add-on modules remained after add-on removal, and that after installation install tasks are also not present in the modules cache.Known issues with pull request:
I've decided not to add a change log entry since issues fixed by this PR occur pretty rarely (I have encountered them only because I have been installing / removing the same add-ons over and over without restarting NVDA when working on another PRs) and describing what exactly has been fixed in a concise way seems difficult here.
Code Review Checklist: