Repository navigation
Support preserving path meaning in os.path.normpath() and abspath() #88482
Description
Activity
>>> os.path.normpath('a/./b/../c//.') 'a/c' >>> pathlib.Path('a/./b/../c//.') PosixPath('a/b/../c')
pathlib takes care not to change the meaning of the path when normalising. That means preserving '..' entries, as these can't be simplified without resolving symlinks etc.
normpath(), on the other handle, /always/ eliminates '..' entries, which can change the meaning of the path.
We could add a new argument to
normpath()andabspath()that leaves '..' entries intact. This was closed as "won't fix" back in bpo-2289, but I think it's worth re-considering.This enhancement would be helpful for my longer-term work to make pathlib an OOP wrapper of os + os.path, rather than a parallel implementation.
- addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement
on Jun 4, 2021 I think you should propose this for discussion on python-ideas list to try for more support. If you can, say more about why reconsider.
Thanks Terry, I've added a topic here: https://discuss.python.org/t/pathlib-and-os-path-code-duplication-and-feature-parity/9239
The bit about
normpath()is towards the middle of the post.For this bug specifically, the pathlib docs describe the desirable behaviour:
<quote>
Spurious slashes and single dots are collapsed, but double dots ('..') are not, since this would change the meaning of a path in the face of symbolic links:
>>> PurePath('foo//bar') PurePosixPath('foo/bar') >>> PurePath('foo/./bar') PurePosixPath('foo/bar') >>> PurePath('foo/../bar') PurePosixPath('foo/../bar')
(a naïve approach would make PurePosixPath('foo/../bar') equivalent to PurePosixPath('bar'), which is wrong if foo is a symbolic link to another directory)
</quote>
single dots are collapsed
For pathlib, I've previously discussed a desire to retain a leading dot component from the initializing path. This could be implemented in strict mode for normpath().
A leading dot is significant in the path of an executable in a search context, such as the first item in the args sequence of subprocess.Popen(). For example, if "./spam" is normalized as "spam", then the system will search the PATH directories instead of the current working directory. It's also import to note that in Windows a leading dot component is required in order to preclude searching even if the path contains slashes. For example, CreateProcessW() and SearchPathW() will try to resolve r"spam\eggs.exe" against every directory in the search path, whereas r".\spam\eggs.exe" is explicitly relative to just the current working directory.
Retaining a leading dot component can also be important in Windows in order to disambiguate a drive-relative path from a named data stream of a file that has a single-letter filename. For example, "C:spam" is a file named "spam" in the working path on drive "C:" (e.g. "C:spam" -> r"C:\working\path\spam"), but r".\C:spam" is a data stream named "spam" in a file named "C" in the current working directory.
I think I agree
How would you feel about two new arguments? Following
os.curdirandos.pardirnames:def normpath(path, *, keep_curdir=False, keep_pardir=False) ...
I think separate keep_curdir and keep_pardir options is over-complicating the signature. Also, I'd prefer to remove a dot component if it's not the first component since there's no reason to keep it.
If you plan to use normpath() in pathlib, then the case for special_prefixes in ntpath.normpath() should be removed. Actually, it never should have been added. IIRC it was added as a workaround for a buggy implementation that's no longer an issue. Only \\?\ is special, and that's only when opening/accessing a path. It's not special in GetFullPathNameW(), as is called by ntpath.abspath() in Windows. This needlessly introduces inconsistency for ntpath.abspath() calls in Windows vs Unix.
It's reasonable/safe for
abspath()to eliminate leading../segments from the given path, becauseos.getcwd()is guaranteed to return a path without symlinks.>>> os.getcwd() '/home/barney' >>> os.path.abspath('../foo') '/home/foo' # ok >>> os.path.abspath('foo/..') '/home/barney' # wrong if `foo/` is a symlink
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsTodo
keep_curdirandkeep_pardirarguments toos.path.normpath()#26694Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields: