Uh oh!
There was an error while loading. Please reload this page.
gh-100809: Fix handling of drive-relative paths in pathlib.Path.absolute() - #100812
Conversation
….absolute() If it's available, use `nt._getfullpathname()` to retrieve an absolute path. This allows paths such as 'X:' to be made absolute even when `os.getcwd()` returns a path on another drive. It follows the behaviour of `os.path.abspath()`, except that no path normalisation is performed.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
eryksun
commented
Jan 7, 2023
Are you planning to also fix this in 3.10 and 3.11? That requires fixing Maybe the fix for |
I fixed this in #95450 and we decided not to backport because it was seemed like quite an obscure bug. I'd like to do the same here as the bug seems equally obscure (there are no user-logged bugs on the tracker that I could find), but I don't mind backporting both if we collectively feel that it's important. |
barneygale
commented
Jan 7, 2023
With fresher eyes I realise my tests don't exercise per-drive working directories. One way to address this would be to add a |
eryksun
commented
Jan 7, 2023
Maybe skip fixing this in 3.10, but |
Maybe, but the bug is observable just from I don't think the presence of this bug in 3.11 makes Does that seem reasonable? |
eryksun
commented
Jan 7, 2023
The "subst.exe" app calls I wouldn't want to announce the new drive in the current session (i.e. try:
importctypesexceptImportError:
defsubst_drive(path):
raiseNotImplementedError('ctypes is not available')
else:
importcontextlibimportstringERROR_FILE_NOT_FOUND=2DDD_REMOVE_DEFINITION=2DDD_EXACT_MATCH_ON_REMOVE=4DDD_NO_BROADCAST_SYSTEM=8kernel32=ctypes.WinDLL('kernel32', use_last_error=True)
@contextlib.contextmanagerdefsubst_drive(path):
"""Temporarily yield a substitute drive for a given path."""forcinreversed(string.ascii_uppercase):
drive=f'{c}:'if (notkernel32.QueryDosDeviceW(drive, None, 0) andctypes.get_last_error() ==ERROR_FILE_NOT_FOUND):
breakelse:
raiseFileExistsError('no available logical drive')
ifnotkernel32.DefineDosDeviceW(
DDD_NO_BROADCAST_SYSTEM, drive, path):
raisectypes.WinError(ctypes.get_last_error())
try:
yielddrivefinally:
ifnotkernel32.DefineDosDeviceW(
DDD_REMOVE_DEFINITION|DDD_EXACT_MATCH_ON_REMOVE,
drive, path):
raisectypes.WinError(ctypes.get_last_error())Footnotes
|
barneygale
commented
Jan 7, 2023
Could you expand on why this is a bad idea please? Thank you! |
eryksun
commented
Jan 7, 2023
This is a drive that we're creating for our own temporary use. I don't think it's appropriate to have Explorer windows automatically updated to include it. I also wouldn't want unknown complications from what any other application might do when sent a Also, it's more efficient to directly call |
Gotcha, thanks. Is there a time-of-check to time-of-use issue with your code above? I wonder if we could skip the |
eryksun
commented
Jan 7, 2023
I don't know how to avoid the small race condition without resorting to the NT API. "subst.exe" doesn't handle this any better.
>>> flags =DDD_NO_BROADCAST_SYSTEM
>>> kernel32.DefineDosDeviceW(flags, 'Z:', 'C:\\Windows')
1
>>> kernel32.DefineDosDeviceW(flags, 'Z:', 'C:\\ProgramData')
1
>>> kernel32.DefineDosDeviceW(flags, 'Z:', 'C:\\Temp')
1
>>> os.path.samefile('Z:', 'C:\\Temp')
True
>>> flags =DDD_REMOVE_DEFINITION|DDD_EXACT_MATCH_ON_REMOVE
>>> kernel32.DefineDosDeviceW(flags, 'Z:', 'C:\\Temp')
1
>>> os.path.samefile('Z:', 'C:\\ProgramData')
True
>>> kernel32.DefineDosDeviceW(flags, 'Z:', 'C:\\ProgramData')
1
>>> os.path.samefile('Z:', 'C:\\Windows')
TrueI think this is bad behavior. The new drive definition affects all processes in the current logon session (i.e. the local device context). I wouldn't want to mask an existing drive definition. At least redefining a system drive requires administrator access. |
Co-authored-by: Eryk Sun <eryksun@gmail.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Eryk Sun <eryksun@gmail.com>
Uh oh!
There was an error while loading. Please reload this page.
…697UT.rst Co-authored-by: Steve Dower <steve.dower@microsoft.com>
zooba
left a comment
There was a problem hiding this comment.
The macOS failure is known and unrelated, so this is good to merge if the rest of CI agrees.
miss-islington
commented
Feb 17, 2023
Thanks @barneygale for the PR, and @zooba for merging it 🌮🎉.. I'm working now to backport this PR to: 3.10. |
miss-islington
commented
Feb 17, 2023
Thanks @barneygale for the PR, and @zooba for merging it 🌮🎉.. I'm working now to backport this PR to: 3.11. |
miss-islington
commented
Feb 17, 2023
Sorry, @barneygale and @zooba, I could not cleanly backport this to |
miss-islington
commented
Feb 17, 2023
Sorry @barneygale and @zooba, I had trouble checking out the |
miss-islington
commented
Feb 17, 2023
Thanks @barneygale for the PR, and @zooba for merging it 🌮🎉.. I'm working now to backport this PR to: 3.11. |
miss-islington
commented
Feb 17, 2023
Sorry, @barneygale and @zooba, I could not cleanly backport this to |
zooba
commented
Feb 17, 2023
@barneygale Can I leave the backports with you? |
bedevere-bot
commented
Feb 17, 2023
GH-101993 is a backport of this pull request to the 3.11 branch. |
…ib.Path.absolute() (pythonGH-100812) Resolving the drive independently uses the OS API, which ensures it starts from the current directory on that drive.. (cherry picked from commit 072011b) Co-authored-by: Barney Gale <barney.gale@gmail.com>
barneygale
commented
Feb 17, 2023
zooba
commented
Feb 20, 2023
Yeah, skip it. |
If it's available, use
nt._getfullpathname()to retrieve an absolute path. This allows paths such as 'X:' to be made absolute even whenos.getcwd()returns a path on another drive. It follows the behaviour ofos.path.abspath(), except that no path normalisation is performed.