Just noting that PurePath.parser was added in the just-released 3.13.0, rather than being an upcoming feature for 3.14 next year.
To save folks clicking the link:
os.path is an alias for either ntpath or posixpath, depending on the current platform.
In 3.13+, the parser attribute on PurePath and its subclasses references the appropriate module for that particular class (the classes with no platform prefix in their names will report the same module as os.path does, since they’re just platform-dependent aliases for the concrete platform-specific types):
This feature doesn’t help with replacing from os import fspath, though, as fspath is a platform independent API that lives directly in os, it isn’t part of the platform dependent functionality that is provided by ntpath and posixpath.
Given the PurePath.as_posix() precedent, the most plausible potential API for a pathlib level os.fspath equivalent is a new PurePath.as_fspath() feature (essentially a public alias for __fspath__, but specified as always returning str and never bytes).
Given the PurePath.as_posix() precedent, the most plausible potential API for a pathlib level os.fspath equivalent is a new PurePath.as_fspath() feature (essentially a public alias for __fspath__, but specified as always returning str and never bytes).
That’s a bit longer, but it’s also more explicit. I’d be fine with [1] either PurePath.fspath() or PurePath.as_fspath().
If a method to convert Path objects to string gets added, I would probably prefer Path.as_str(). Using fspath would add a bit of disparity imo, since os.fspath can return either string or bytes while Path.as_fspath will always return a string.
If you know that you have a PurePath object (e.g. because you created it yourself), it’s perfectly reasonable and safe to use str(path). That’s the usual way of stringifying objects in Python and it works fine here.
If your API accepts path-like objects (i.e. str, bytes, PurePath, or anything implementing __fspath__), then os.fspath() is the way to go. It’s only a minor burden to import os at the top of a script IMO!
The strongest argument I can see is that str() works on every object that doesn’t raise an exception. If you attempt to use a method, then only things that provide that specific method will succeed, and the rest will raise AttributeErrors. I can see that feeling “cleaner” than str().
I still don’t think this warrants adding yet another method of retrieving the same data. Checking the type or using os.fspath() is fine. I don’t understand the complaints about importing os seeming excessive or the mode of access needing to be a method and not a stdlib function.
I feel there’s an uneasy context switch from working on the RHS of a path expression then to the left, only to wrap it with str() call. This seems to happen often enough. 3rd party module functions that only accept path strings are the usual culprits. Depending on your workflow, it involves seeking to the start of your original path expression, inserting “str(” then seeking the end of your expression and inserting a “)”. This gets a bit cumbersome once you have a fairly long chain of object method calls on your path object. The example below isn’t so unusual.
To clarify – does this case also apply to PurePath subclasses’ objects – specifically PosixPath and WindowsPath? Is it safe to use str(path) with them knowing for sure their type?
fspath(path): This is the most explicit spelling. It returns a filesystem path representation or raises an error. Any object that doesn’t define __fspath__ will error, which I think is the correct behaviour. When you look at a random fspath(obj) you immediately know that it’s guranteed to be a filesystem path.
str(path): This works for pathlib.Path because their __str__ is equivalent to __fspath__, but it will also accept any object that defines __str__.
path.fullpath: I don’t like this name at all. full implies it will be an absolute path (str(path.resolve())) when it’s equivalent to str(path). path also implies that it will be a Path when it’s a string.
I think fspath() is both the most explicit and Pythonic approach here. Any Python user aware of dunder methods is already comfortable with the idea of a __foobar__ method and its accompanying foobar() function that calls said method.