fix module cache cleanup in Addon._cleanupAddonImports - #20715
Merged
Conversation
Collaborator
Author
|
cc @nvdaes and @lukaszgo1 because this touches your code. |
LeonarddeR
force-pushed
the
fixCleanupAddonImports
branch
from
August 22, 2026 10:16
662862c to
dad8414
Compare
…ddonImports _cleanupAddonImports now matches modules on their file path against the add-on directory, case insensitively and anchored at a path separator. Modules without a file attribute are skipped. Add unit tests covering the cleanup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LeonarddeR
force-pushed
the
fixCleanupAddonImports
branch
from
August 25, 2026 06:38
dad8414 to
e487581
Compare
LeonarddeR
marked this pull request as ready for review
August 25, 2026 06:38
LeonarddeR
requested review from
nvdaes and
seanbudd
and
a lite review from Copilot
August 25, 2026 06:38
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes add-on module cache cleanup after running install/uninstall tasks, ensuring NVDA reliably purges sys.modules entries that originate from an add-on’s directory (including transitively imported modules). This prevents an updated add-on from accidentally reusing stale, cached modules from a previously installed version.
Changes:
- Fix
Addon._cleanupAddonImportsto identify add-on modules viamodule.__file__(safely viagetattr), with case-normalized path comparison and a directory-boundary-safe prefix. - Add a focused unit test suite covering transitive imports, case-insensitive matching, missing/
None__file__, sibling-directory false positives, and clearing of tracked imports. - Add a user-facing changelog entry describing the fix to add-on updating behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
user_docs/en/changes.md |
Adds release note explaining the add-on update reliability fix. |
tests/unit/test_addonHandler/test_addonImports.py |
Adds unit tests to prevent regressions in add-on import cache cleanup. |
source/addonHandler/__init__.py |
Corrects the cleanup scan to match modules by normalized __file__ path within the add-on directory. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
seanbudd
reviewed
Aug 25, 2026
seanbudd
approved these changes
Aug 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Link to issue number:
Fixup for #18971.
Summary of the issue:
Installing or removing an add-on can run install tasks. These tasks can import modules from the add-on. Python caches every import in
sys.modules. NVDA must drop the add-on's modules from that cache afterwards. Otherwise a later import returns the cached old module instead of the file on disk.Modules the tasks load directly are tracked in
_importedAddonModulesand dropped reliably. Modules imported indirectly, for example through a relative import inside the add-on, are not tracked. For those,_cleanupAddonImportsscans every module imported during the task and drops each one whose file is inside the add-on directory.That scan has been broken twice:
module.__file__directly. Modules without a__file__, such as built-in modules, made it crash withAttributeError.module.__name__instead. A module name is not a file path, so the scan matches nothing. Since 2026.1 it drops nothing.Result: updating an add-on can break on the first start after the update. The old version's uninstall task runs first and leaves old modules in the cache. The new version then partly imports old code. A second restart of NVDA resolves the situation regardless.
Description of user facing changes:
Updated add-ons that perform install tasks no longer raise intermittent errors on the first start of NVDA after the update.
Description of developer facing changes:
None.
Description of development approach:
The approach of #15967 was the correct one: a module belongs to the add-on when its file is inside the add-on directory. This change returns to matching on the file path and fixes the three defects of that implementation:
getattr(module, "__file__", None). Modules without a__file__are skipped instead of crashing the scan, so the error fixed by Fix bug when updating add-ons automatically #18971 stays fixed.os.path.normcase, so case differences match.myAddonExtrano longer matchesmyAddon.Each defect is covered by a unit test. The loop over the tracked modules is unchanged.
Testing strategy:
New unit tests in
tests/unit/test_addonHandler/test_addonImports.pybuild an add-on in a temporary directory. One of its modules imports a sibling module. Checked:__file__is kept and nothing raises,__file__ = Noneis kept,Both historical scans fail this suite. The #18971 form fails the two drop checks. The #15967 form crashes on the missing
__file__check, wrongly drops the sibling-directory module and misses the case difference.Known issues with pull request:
None.
Code Review Checklist: