Restore `JoinablePath.[is_]relative_to()`, which were deleted in
ef63cca494. These methods are too useful to
forgo. Restore old tests, and add new tests covering path classes with
non-overridden `__eq__()` and `__hash__()`.
Slightly simplify `PurePath.relative_to()` while we're in the area.
No change to public APIs, because the pathlib ABCs are still private.
Rename `pathlib._os.magic_open()` to `vfsopen()`. The new name is a bit
less abstract, and it aligns with the `vfspath()` method added in 5dbd27d.
Per discussion on discourse[^1], adjust `vfsopen()` so that the following
methods may be called:
- `__open_reader__()`
- `__open_writer__(mode)`
- `__open_updater__(mode)`
These three methods return readable, writable, and full duplex file objects
respectively. In the 'writer' method, *mode* is either 'a', 'w' or 'x'. In
the 'updater' method, *mode* is either 'r' or 'w'.
In the pathlib ABCs, replace `ReadablePath.__open_rb__()` with
`__open_reader__()`, and replace `WritablePath.__open_wb__()` with
`__open_writer__()`.
[^1]: https://discuss.python.org/t/open-able-objects/90238
Co-authored-by: Petr Viktorin <encukou@gmail.com>
In the abstract interface of `JoinablePath`, replace `__str__()` with
`__vfspath__()`. This frees user implementations of `JoinablePath` to
implement `__str__()` however they like (or not at all.)
Also add `pathlib._os.vfspath()`, which calls `__fspath__()` or
`__vfspath__()`.
Ensure that warnings about unspecified text encodings are emitted from
`ReadablePath.read_text()`, `WritablePath.write_text()` and `magic_open()`
with the correct stack level set.
In `JoinablePath.full_match()` and `ReadablePath.glob()`, accept a `str`
pattern argument rather than `JoinablePath | str`.
In `ReadablePath.copy()` and `copy_into()`, accept a `WritablePath` target
argument rather than `WritablePath | str`.
In the private pathlib ABCs, replace `_WritablePath._write_info()` with
`_WritablePath._copy_from()`. This provides the target path object with
more control over the copying process, including support for querying and
setting metadata *before* the path is created.
Adjust `_ReadablePath.copy()` so that it forwards its keyword arguments to
`_WritablePath._copy_from()` of the target path object. This allows us to
remove the unimplemented *preserve_metadata* argument in the ABC method,
making it a `Path` exclusive.
Remove the *case_sensitive* argument from `_JoinablePath.full_match()` and
`_ReadablePath.glob()`. Using a non-native case sensitivity forces the use
of "case-pedantic" globbing, where we `iterdir()` even for non-wildcard
pattern segments. But it's hard to know when to enable this mode, as
case-sensitivity can vary by directory, so `_PathParser.normcase()` doesn't
always give the full picture. The `Path.glob()` implementation is forced to
make an educated guess, but we can avoid the issue in the ABCs by dropping
the *case_sensitive* argument.
(I probably shouldn't have added these arguments in `PurePath` and `Path`
in the first place!)
Also drop support for `_ReadablePath.glob(recurse_symlinks=False)`, which
makes recursive globbing much slower.
In `pathlib.types._JoinablePath.full_match()`, treat alternate path
separators in the path and pattern as if they were primary separators. e.g.
if the parser is `ntpath`, then `P(r'foo/bar\baz').full_match(r'*\*/*')` is
true.
In `pathlib.types._JoinablePath.with_name()`, retain any alternative path
separator preceding the old name, rather stripping and replacing it with a
primary separator. As a result, this method changes _only_ the name.
There used to be a meaningful distinction between these modules: `pathlib`
imported `pathlib._abc` but not `pathlib.types`. This is no longer the
case (neither module is imported), so we move the ABCs as follows:
- `pathlib._abc.JoinablePath` --> `pathlib.types._JoinablePath`
- `pathlib._abc.ReadablePath` --> `pathlib.types._ReadablePath`
- `pathlib._abc.WritablePath` --> `pathlib.types._WritablePath`
Add `pathlib.Path.info` attribute, which stores an object implementing the `pathlib.types.PathInfo` protocol (also new). The object supports querying the file type and internally caching `os.stat()` results. Path objects generated by `Path.iterdir()` are initialised with status information from `os.DirEntry` objects, which is gleaned from scanning the parent directory.
The `PathInfo` protocol has four methods: `exists()`, `is_dir()`, `is_file()` and `is_symlink()`.