Normalize bare drive letters in portable copy directory (#20159)#20446
Normalize bare drive letters in portable copy directory (#20159)#20446Mubashir78 wants to merge 6 commits into
Conversation
When entering a bare drive letter (e.g. 'c:') as the destination directory in the Create Portable NVDA dialog, os.path.isabs returns False because 'c:' lacks a trailing separator. Normalize by appending os.sep so the path is recognised as absolute and the portable copy proceeds without error. Adds unit tests for the normalization logic, mocking drive-letter semantics to remain platform-independent.
|
Hi - please read our contributing guidelines and fill out the PR template. I've added it back to the PR for you. |
|
Hi @Mubashir78 - this isn't quite the approach suggested in the comments. |
|
Thanks for the feedback. I have reworked the approach based on the discussion in #20159 -- instead of normalizing bare drive letters, the dialog now shows a specific error message explaining that 'c:' is not valid and suggesting 'c:' instead. The PR template has been filled out and a changelog entry added. |
|
Hi - I've added back the code review checklist here as well. can you please confirm what manual testing you have performed to ensure the user facing changes are working as expected? |
|
Filled out the checklist. Manual testing:
I hope all checks out 👍 |
|
You have removed the description of the unit tests, can you make sure to include description of all tests (unit and manual). |
|
My bad, I'm having a hard time making sure i make no mistakes in formal English. Usually my strategy of creating PRs has been minimally explaining the changes I've introduced, where I've introduced them, what steps I took to reproduce and how they overall improve the code flow. |
Move _mockSplitdrive and _mockIsabs to module level, inline _msgBox as lambda. Addresses review feedback on nvaccess#20446.
|
Done:
|
| self.assertEqual(installer._comparePreviousInstall(), installer.ComparisonState.UPGRADE) | ||
|
|
||
|
|
||
| def _mockSplitdrive(p: str) -> tuple: |
There was a problem hiding this comment.
| def _mockSplitdrive(p: str) -> tuple: | |
| def _mockSplitdrive(p: str) -> tuple[str, str]: |
|
@Mubashir78 you write:
I guess you meant "c:", not "c:". If yes, please fix it. Also, have you tested with "c:folder" (without backslash), which is a valid way to define a non absolute folder. What is the error message in this case? |
|
I assume you meant to type "c:", which is a valid path, and yes i have fixed that typo now.
Yes i have, and confirmed that it falls through to the standard not absolute error. Since splitdrive returns ('c:', 'folder'), the bare drive check does not trigger and the existing error path handles it. |
SaschaCowley
left a comment
There was a problem hiding this comment.
I'm a little bit confused by the approach here:
-
The rule isn't that a drive letter, followed by a colon and nothing else refers to the CWD. The drive letter and colon tell Windows which drive you are referring to; what follows is the path on that drive. So anything that follows the drive letter and colon that isn't a root path isn't absolute.
PS C:\Users\SaschaCowley> cd c: PS C:\Users\SaschaCowley> cd c:documents PS C:\Users\SaschaCowley\Documents> cd c:nonexistant Set-Location: Cannot find path 'C:\Users\SaschaCowley\Documents\nonexistant' because it does not exist. PS C:\Users\SaschaCowley\Documents> cd d: PS D:\> cd d:projects PS D:\projects> cd c: PS C:\Users\SaschaCowley\Documents> cd d:nvda PS D:\projects\nvda>
I would suggest having a look at the implementation of
ntpath.isabs. Or potentially just switching the logic to something like:if not os.path.isabs(expandedPortableDirectory ): if not os.path.splitroot(expandedPortableDirectory)[1]: message = ... # bare mount point else: message = ... # Path without mount point gui.messageBox(message, ...) return
Note that UNC paths are always absolute.
-
I'm not sure I understand why all the rigmarole around creating mocked
os.pathfunctions.- The functions you're mocking don't actually touch the file system, so there's no need to mock them;
- NVDA's unit tests can only run on Windows, so it's not like there will be issues where the tests fail because
posixpathis used inadvertently; and - Even if you want to be extra sure that you're using Windows paths, you ccan use
ntpath.
-
Rather than mocking the GUI, wouldn't it be easier to extract the path check into a helper function that either returns the expanded and validated path, or raises pre-defined exceptions (possibly via
gui.message.DisplayableError)?
…root per SaschaCowley review - Replace splitdrive with splitroot for precise mount point detection - Extract inline validation into _validatePortableDirectory helper - Two distinct error messages: bare drive (c:, c:foo) vs no drive (foo) - onCreatePortable now delegates to helper - Tests directly test helper with ntpath, no os.path mocking needed
9decd51 to
b49e67f
Compare
|
Thanks for the reviews. I have reworked the approach based on the feedback: @SaschaCowley all three points are addressed in the latest push:
mount, root, _ = os.path.splitroot(expandedPortableDirectory)
if mount and not root:
# Bare mount point (c:, c:folder)Now correctly catches both
patch("gui.installerGui.os.path.isabs", side_effect=ntpath.isabs)
patch("gui.installerGui.os.path.splitroot", side_effect=ntpath.splitroot)
@CyrilleB79 re @seanbudd kept the code review checklist intact with parent boxes checked and added description of all tests in the PR body. |
|
May I ask why this PR has been drafted @seanbudd ? |
b49e67f to
50743c7
Compare
50743c7 to
b49e67f
Compare
Fixes #20159
Link to issue number:
#20159
Summary of the issue:
When entering a bare drive letter (e.g.
c:) as the destination directory in the Create Portable NVDA dialog, the error message is confusing -- it says the path must start with a drive letter even though the user did provide one.Description of user facing changes:
Instead of the generic "not absolute" error, the dialog now shows a specific message: "A drive letter without a trailing backslash (e.g. "c:") is not a valid destination directory. Please use "c:" instead." This makes it clear to the user why
c:is not accepted and what they should type instead.Description of developer facing changes:
No developer-facing changes.
Description of development approach:
Detect bare drive letters using os.path.splitdrive (when a drive letter is found with no trailing path), and show a targeted error message explaining that
c:refers to the current directory on drive C, not its root, and suggestingc:\instead.Testing strategy:
Unit tests:
c:\is shownManual testing:
Known issues with pull request:
None.
Code Review Checklist: