ISTM that os.PathLike
is doing double-duty:
- Majority usage: a local path with a string/bytes representation
- e.g. when passed to
io.open()
,os.mkdir()
, etc
- e.g. when passed to
- Minority usage: any kind of path with a string/bytes representation
- e.g. when passed to
os.path.join()
,os.path.split()
, etc
- e.g. when passed to
In some situations the distinction might matter. For example, it’s perfectly legitimate to take the posixpath.basename()
of a zip file member path, but much less advisable to call posixpath.islink()
in the same context. Yet the function signatures don’t reflect the difference. Another such case is pathlib.PurePath
, which “provides path-handling operations which don’t actually access a filesystem” according to its docs, yet provides a __fspath__()
method that enables those filesystem-accessing operations:
>>> Path('...').unlink() # success
>>> PurePath('...').unlink() # error: no such method.
>>> os.unlink(Path('...')) # success
>>> os.unlink(PurePath('...')) # success... wait PurePath supports I/O?!
IMHO the last case here should (eventually) be an error, but to do that we’d need to deprecate/remove PurePath.__fspath__()
(and make it a Path
-only thing), and if we did that, then purely lexical function like os.path.join()
would begin rejecting PurePath
objects for no good reason.
This suggests to me we need something like os.fspath()
/ os.PathLike
/ __fspath__()
but for pure/virtual paths. I humbly suggest os.vfspath()
/ os.VirtualPathLike
/ __vfspath__()
, where VirtualPathLike
is a superclass of PathLike
. The os.path
functions listed in the first half of this GH issue could be made to call os.vfspath()
, as they do only lexical work.
I bring this up because, in the pathlib ABCs I’m developing (discussion, pre-PEP), I declare JoinablePath.__str__()
as an abstract method. I can’t use __fspath__()
because this type shouldn’t be accepted by os.unlink()
etc. But __str__()
doesn’t feel quite right either - for one thing, users might want a string representation that isn’t just a path - perhaps they’d like to use a URL instead for example. I believe what I really need is a __vfspath__()
dunder to serve as an abstract method in JoinablePath
.
Thank you for reading. I’d love to hear your thoughts/concerns/ideas!
P.S. Deprecating PurePath.__fspath__()
in favour of PurePath.__vfspath__()
might not be practical even if we agree it’s desirable from a typing perspective (and I’m not taking that for granted either!). I bring it up as a class that will be familiar to many readers; my immediate concern is with JoinablePath
though.