-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
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
Document various options for getting the absolute path from pathlib.Path objects #83271
Comments
The question on how best to get an absolute path from a pathlib.Path object keeps coming up (see https://bugs.python.org/issue29688, https://discuss.python.org/t/add-absolute-name-to-pathlib-path/2882/, and https://discuss.python.org/t/pathlib-absolute-vs-resolve/2573 as examples). As pointed out across those posts, getting the absolute path is surprisingly subtle and varied depending on your needs. As such we should probably add a section somewhere in the pathlib docs explaining the various ways and why you would choose one over the other. |
Yes Please! I'd offer to help, but I really don't get the intricacies involved. I will offer to proofread and copy-edit though, if that's helpful. And I note that coincidentally, just in the last week, I needed to make an absolute path from a Path, and it took me far too long to figure out that .resolve() would do it for me. Then I needed to do it again three days later, and it again took a while -- "resolve" is simply not mnemonic for me, and I'm guessing a lot of people have the same issue. And I didn't find .absolute(), cause it's not documented. I see in issue bpo-29688 that there are reasons for that, but I'll make a plea: Please document .absolute(), even if those docs say something like "may not work in all circumstances, not well tested". Alternatively, if it's decided that folks should just use .resolve() in all cases anyway, then make .absolute() an alias for .resolve(). Or if that's not a good option, then at least put some prominent notes in resolve() so people will find it. Also -- I needed to read the resolve() docs carefully (and then test) to see if it was what I wanted - which I know, is what this issue is about. In short -- I understand that this is a complex issue, but making an absolute path is a pretty common use case, and we've had os.path.abspath() for decades, so there should be one obvious way to do it, and it should be easily discoverable. NOTE: even if there is no one to do the work of properly testing .absolute() at this point, it would b nice to at least decide now what the long term goal is -- will there be an absolute() or is resolve() all we really need? |
+1 on this. Given that (as far as I can tell from the various discussions) (More subtle questions like UNC path vs drive letter, mentioned on the Discourse thread, are probably things that we can defer to a "more advanced cases" discussion in the docs). |
I've written an "Absolute paths" section based on the knowledge I found in the various threads. Any review is appreciated. https://github.com/florisla/cpython/tree/pathlib-chapter-absolute-paths With some related documentation changes: https://github.com/florisla/cpython/tree/absolute-path-related-improvements |
You've provided links to your branches, but not to the specific text you're proposing to add. Can you link to a diff or something that shows what you've added more precisely? |
This is the new chapter: |
If we want something mnemonic, I'm sure nothing beats __abs__. (Hey, we have __truediv__ already!;) |
Could you review the proposed addition to the documentation? |
(sorry, didn't see the GitHub comments before... I'll process those first.) |
Based on the feedback received in GitHub here: I made a new revision of the 'Absolute paths' chapter here: Further feedback is welcome. Changes:
For brevity, I've kept the wording on substitute drive and handling of For the same reason I did not not include eryksun's (interesting!) info Not mentioning Path.resolve()'s behavior w.r.t. non-existing files since |
I don't see it mentioned in the documentation of Linux:
Windows:
|
First, I hope we all agree: It is very confusing between these two, but despite claims otherwise, absolute() does not work as expected. However, to sum up the findings below:
Done on Windows 10, python 3.9.5 >>> ini
WindowsPath('desktop.ini/..')
>>> ini.resolve().is_absolute()
True
>>> ini.absolute()
WindowsPath('C:/Users/zim/Downloads/desktop.ini/..')
>>> ini.absolute().is_absolute()
True This second should not be True, there is a trailing '..' not resolved by absolute() Now let's create a truly messy path:
>>> ini.resolve()
WindowsPath('C:/Users/zim/Downloads')
>>> ini = ini / "ntuser.ini"
>>> ini.exists()
False
>>> ini.resolve()
WindowsPath('C:/Users/zim/Downloads/ntuser.ini')
>>> ini = ini / "../ntuser.ini"
>>> ini.exists()
False
>>> ini.resolve()
WindowsPath('C:/Users/zim/Downloads/ntuser.ini')
>>> ini = ini / "../../ntuser.ini"
>>> ini.resolve()
WindowsPath('C:/Users/zim/ntuser.ini')
>>> ini.exists()
True
>>> ini.absolute()
WindowsPath('C:/Users/zim/Downloads/desktop.ini/../ntuser.ini/../ntuser.ini/../../ntuser.ini')
>>> ini.absolute().is_absolute()
True absolute() not only doesn't give an absolute path, but is_absolute() is somehow ok with that. Now a file that doesn't exist:
>>> mike = Path("palin.jpg")
>>> mike.resolve()
WindowsPath('palin.jpg')
>>> mike.resolve().is_absolute()
False
>>> mike.absolute()
WindowsPath('C:/Users/zim/Downloads/palin.jpg')
>>> mike.absolute().is_absolute()
True Finally, absolute() is right about the right thing, but resolve() is not terribly wrong. is_absolute() is correctly False here (for once). The problem is that the after a resolve() call, a Path object can still be used to create a file (good), but if resolve() is used before file creation, then the full path will not be there as should be expected (bad). This seems like a bug with resolve() What if a file is non existent AND relative? Things get more confusing. >>> badrel = Path('../circus.jpg')
>>> badrel
WindowsPath('../circus.jpg')
>>> badrel.absolute()
WindowsPath('C:/Users/zim/Downloads/../circus.jpg')
>>> badrel.resolve()
WindowsPath('C:/Users/zim/circus.jpg')
>>> badrel.exists()
False So, absolute() still acts like the normal trash fire it is with relative paths, but what's this, resolve() actually gives an absolute path?! I should note resolve() only behaves unpredictably on Windows. It correctly resolves non-existent files no matter what on macOS and Linux (caveat: my linux test was done with python 3.6). However, absolute() always fails to distill paths with relative steps regardless of OS. So, it seems clear: |
I don't agree. To me, absolute means regardless of a reference point. So, absolute path would be a path that refers to the same entity from whichever directory you reference it. And that is surely the case for these two. |
This is a bug in resolve(). It was fixed in 3.10+ by switching to ntpath.realpath(). I don't remember why a fix for 3.9 was never applied. Work on the PR may have stalled due to a minor disagreement.
No, a relative path depends on either the current working directory or, for a symlink target, the path of the directory that contains the symlink. In Windows, a rooted path such as r"\spam" is a relative path because it depends on the drive of the current working directory. For example, if the current working directory is r"Z:\eggs", then r"\spam" resolves to r"Z:\spam". Also, a drive-relative paths such as "Z:spam" depends on the working directory of the given drive. Windows supports a separate working directory for each drive. For example, if the working directory of drive "Z:" is r"Z:\eggs", then "Z:spam" resolves to r"Z:\eggs\spam". |
The docs for PurePath.is_absolute() say:
This does not preclude it from having ".." segments. PurePath.absolute() is documented as of bpo-29688 / 3.11, see: https://docs.python.org/3.11/library/pathlib.html#pathlib.Path.absolute The documentation for the absolute() method is deliberately placed alongside resolve() for ease of comparison. Both methods make a path absolute, but resolve() also follows symlinks, and consequently is able to safely elide ".." segments. |
on python3.9 - it should make the path absolute, but it doesn't. python/cpython#83271
Completed in #26153 |
Note: 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:
The text was updated successfully, but these errors were encountered: