| 
									
										
										
										
											2024-01-13 08:47:00 +00:00
										 |  |  | """
 | 
					
						
							|  |  |  | Abstract base classes for rich path objects. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This module is published as a PyPI package called "pathlib-abc". | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This module is also a *PRIVATE* part of the Python standard library, where | 
					
						
							|  |  |  | it's developed alongside pathlib. If it finds success and maturity as a PyPI | 
					
						
							|  |  |  | package, it could become a public part of the standard library. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  | Three base classes are defined here -- JoinablePath, ReadablePath and | 
					
						
							|  |  |  | WritablePath. | 
					
						
							| 
									
										
										
										
											2024-01-13 08:47:00 +00:00
										 |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  | from abc import ABC, abstractmethod | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  | from glob import _PathGlobber, _no_recurse_symlinks | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  | from pathlib import PurePath, Path | 
					
						
							| 
									
										
										
										
											2025-02-17 19:15:25 +00:00
										 |  |  | from pathlib._os import magic_open, CopyWriter | 
					
						
							| 
									
										
										
										
											2024-08-11 22:43:18 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-22 01:41:38 +00:00
										 |  |  | def _explode_path(path): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Split the path into a 2-tuple (anchor, parts), where *anchor* is the | 
					
						
							|  |  |  |     uppermost parent of the path (equivalent to path.parents[-1]), and | 
					
						
							|  |  |  |     *parts* is a reversed list of parts following the anchor. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     split = path.parser.split | 
					
						
							|  |  |  |     path = str(path) | 
					
						
							|  |  |  |     parent, name = split(path) | 
					
						
							|  |  |  |     names = [] | 
					
						
							|  |  |  |     while path != parent: | 
					
						
							|  |  |  |         names.append(name) | 
					
						
							|  |  |  |         path = parent | 
					
						
							|  |  |  |         parent, name = split(path) | 
					
						
							|  |  |  |     return path, names | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  | class JoinablePath(ABC): | 
					
						
							|  |  |  |     """Abstract base class for pure path objects.
 | 
					
						
							| 
									
										
										
										
											2018-02-19 08:36:32 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												GH-110109: Add `pathlib._PurePathBase` (#110670)
Add private `pathlib._PurePathBase` class: a private superclass of both `PurePath` and `_PathBase`. Unlike `PurePath`, it does not define any of these special methods: `__fspath__`, `__bytes__`, `__reduce__`, `__hash__`, `__eq__`, `__lt__`, `__le__`, `__gt__`, `__ge__`. Its initializer and path joining methods accept only strings, not os.PathLike objects more broadly.
This is important for supporting *virtual paths*: user subclasses of `_PathBase` that provide access to archive files, FTP servers, etc. In these classes, the above methods should be implemented by users only as appropriate, with due consideration for the hash/equality of any backing objects, such as file objects or sockets.
											
										 
											2023-12-08 17:39:04 +00:00
										 |  |  |     This class *does not* provide several magic methods that are defined in | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     its implementation PurePath. They are: __init__, __fspath__, __bytes__, | 
					
						
							| 
									
										
										
										
											2024-12-22 01:17:59 +00:00
										 |  |  |     __reduce__, __hash__, __eq__, __lt__, __le__, __gt__, __ge__. | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-12-22 01:17:59 +00:00
										 |  |  |     __slots__ = () | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     @property | 
					
						
							|  |  |  |     @abstractmethod | 
					
						
							|  |  |  |     def parser(self): | 
					
						
							|  |  |  |         """Implementation of pathlib._types.Parser used for low-level path
 | 
					
						
							|  |  |  |         parsing and manipulation. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @abstractmethod | 
					
						
							| 
									
										
										
										
											2023-05-05 20:04:53 +01:00
										 |  |  |     def with_segments(self, *pathsegments): | 
					
						
							|  |  |  |         """Construct a new path object from any number of path-like objects.
 | 
					
						
							|  |  |  |         Subclasses may override this method to customize how new path objects | 
					
						
							|  |  |  |         are created from methods like `iterdir()`. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-22 01:17:59 +00:00
										 |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2023-05-05 20:04:53 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     @abstractmethod | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     def __str__(self): | 
					
						
							|  |  |  |         """Return the string representation of the path, suitable for
 | 
					
						
							|  |  |  |         passing to system calls."""
 | 
					
						
							| 
									
										
										
										
											2024-12-22 01:17:59 +00:00
										 |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def anchor(self): | 
					
						
							|  |  |  |         """The concatenation of the drive and root, or ''.""" | 
					
						
							| 
									
										
										
										
											2024-12-22 01:41:38 +00:00
										 |  |  |         return _explode_path(self)[0] | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def name(self): | 
					
						
							|  |  |  |         """The final path component, if any.""" | 
					
						
							| 
									
										
										
										
											2024-11-05 21:19:36 +00:00
										 |  |  |         return self.parser.split(str(self))[1] | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def suffix(self): | 
					
						
							| 
									
										
										
										
											2019-11-02 18:46:24 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         The final component's last suffix, if any. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This includes the leading period. For example: '.txt' | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-05-25 21:01:36 +01:00
										 |  |  |         return self.parser.splitext(self.name)[1] | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def suffixes(self): | 
					
						
							| 
									
										
										
										
											2019-11-02 18:46:24 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         A list of the final component's suffixes, if any. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         These include the leading periods. For example: ['.tar', '.gz'] | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-05-25 21:01:36 +01:00
										 |  |  |         split = self.parser.splitext | 
					
						
							|  |  |  |         stem, suffix = split(self.name) | 
					
						
							|  |  |  |         suffixes = [] | 
					
						
							|  |  |  |         while suffix: | 
					
						
							|  |  |  |             suffixes.append(suffix) | 
					
						
							|  |  |  |             stem, suffix = split(stem) | 
					
						
							|  |  |  |         return suffixes[::-1] | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def stem(self): | 
					
						
							|  |  |  |         """The final path component, minus its last suffix.""" | 
					
						
							| 
									
										
										
										
											2024-05-25 21:01:36 +01:00
										 |  |  |         return self.parser.splitext(self.name)[0] | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def with_name(self, name): | 
					
						
							|  |  |  |         """Return a new path with the file name changed.""" | 
					
						
							| 
									
										
										
										
											2024-03-31 19:14:48 +01:00
										 |  |  |         split = self.parser.split | 
					
						
							| 
									
										
										
										
											2024-01-14 21:49:53 +00:00
										 |  |  |         if split(name)[0]: | 
					
						
							| 
									
										
										
										
											2023-11-25 17:19:38 +00:00
										 |  |  |             raise ValueError(f"Invalid name {name!r}") | 
					
						
							| 
									
										
										
										
											2024-11-05 21:19:36 +00:00
										 |  |  |         return self.with_segments(split(str(self))[0], name) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-19 17:29:49 +02:00
										 |  |  |     def with_stem(self, stem): | 
					
						
							|  |  |  |         """Return a new path with the stem changed.""" | 
					
						
							| 
									
										
										
										
											2024-02-24 19:37:03 +00:00
										 |  |  |         suffix = self.suffix | 
					
						
							|  |  |  |         if not suffix: | 
					
						
							|  |  |  |             return self.with_name(stem) | 
					
						
							|  |  |  |         elif not stem: | 
					
						
							|  |  |  |             # If the suffix is non-empty, we can't make the stem empty. | 
					
						
							|  |  |  |             raise ValueError(f"{self!r} has a non-empty suffix") | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return self.with_name(stem + suffix) | 
					
						
							| 
									
										
										
										
											2020-04-19 17:29:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     def with_suffix(self, suffix): | 
					
						
							| 
									
										
										
										
											2018-08-03 22:49:42 +02:00
										 |  |  |         """Return a new path with the file suffix changed.  If the path
 | 
					
						
							|  |  |  |         has no suffix, add given suffix.  If the given suffix is an empty | 
					
						
							|  |  |  |         string, remove the suffix from the path. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:25:16 +00:00
										 |  |  |         stem = self.stem | 
					
						
							| 
									
										
										
										
											2024-05-19 17:04:56 +01:00
										 |  |  |         if not stem: | 
					
						
							| 
									
										
										
										
											2024-02-24 19:37:03 +00:00
										 |  |  |             # If the stem is empty, we can't make the suffix non-empty. | 
					
						
							| 
									
										
										
										
											2024-01-30 14:25:16 +00:00
										 |  |  |             raise ValueError(f"{self!r} has an empty name") | 
					
						
							| 
									
										
										
										
											2024-05-25 21:01:36 +01:00
										 |  |  |         elif suffix and not suffix.startswith('.'): | 
					
						
							| 
									
										
										
										
											2023-11-25 17:19:38 +00:00
										 |  |  |             raise ValueError(f"Invalid suffix {suffix!r}") | 
					
						
							| 
									
										
										
										
											2024-05-19 17:04:56 +01:00
										 |  |  |         else: | 
					
						
							|  |  |  |             return self.with_name(stem + suffix) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def parts(self): | 
					
						
							|  |  |  |         """An object providing sequence-like access to the
 | 
					
						
							|  |  |  |         components in the filesystem path."""
 | 
					
						
							| 
									
										
										
										
											2024-12-22 01:41:38 +00:00
										 |  |  |         anchor, parts = _explode_path(self) | 
					
						
							| 
									
										
										
										
											2024-01-09 22:46:50 +00:00
										 |  |  |         if anchor: | 
					
						
							|  |  |  |             parts.append(anchor) | 
					
						
							|  |  |  |         return tuple(reversed(parts)) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-05 20:04:53 +01:00
										 |  |  |     def joinpath(self, *pathsegments): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """Combine this path with one or several arguments, and return a
 | 
					
						
							|  |  |  |         new path representing either a subpath (if all arguments are relative | 
					
						
							|  |  |  |         paths) or a totally different path (if one of the arguments is | 
					
						
							|  |  |  |         anchored). | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-22 01:17:59 +00:00
										 |  |  |         return self.with_segments(str(self), *pathsegments) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __truediv__(self, key): | 
					
						
							| 
									
										
										
										
											2019-08-08 01:41:10 -04:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2024-12-22 01:17:59 +00:00
										 |  |  |             return self.with_segments(str(self), key) | 
					
						
							| 
									
										
										
										
											2019-08-08 01:41:10 -04:00
										 |  |  |         except TypeError: | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __rtruediv__(self, key): | 
					
						
							| 
									
										
										
										
											2019-08-08 01:41:10 -04:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2024-12-22 01:17:59 +00:00
										 |  |  |             return self.with_segments(key, str(self)) | 
					
						
							| 
									
										
										
										
											2019-08-08 01:41:10 -04:00
										 |  |  |         except TypeError: | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def parent(self): | 
					
						
							|  |  |  |         """The logical parent of the path.""" | 
					
						
							| 
									
										
										
										
											2024-11-05 21:19:36 +00:00
										 |  |  |         path = str(self) | 
					
						
							| 
									
										
										
										
											2024-03-31 19:14:48 +01:00
										 |  |  |         parent = self.parser.split(path)[0] | 
					
						
							| 
									
										
										
										
											2024-01-06 21:17:51 +00:00
										 |  |  |         if path != parent: | 
					
						
							| 
									
										
										
										
											2024-12-06 21:39:45 +00:00
										 |  |  |             return self.with_segments(parent) | 
					
						
							| 
									
										
										
										
											2024-01-06 21:17:51 +00:00
										 |  |  |         return self | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def parents(self): | 
					
						
							|  |  |  |         """A sequence of this path's logical parents.""" | 
					
						
							| 
									
										
										
										
											2024-03-31 19:14:48 +01:00
										 |  |  |         split = self.parser.split | 
					
						
							| 
									
										
										
										
											2024-11-05 21:19:36 +00:00
										 |  |  |         path = str(self) | 
					
						
							| 
									
										
										
										
											2024-01-14 21:49:53 +00:00
										 |  |  |         parent = split(path)[0] | 
					
						
							| 
									
										
										
										
											2024-01-06 21:17:51 +00:00
										 |  |  |         parents = [] | 
					
						
							|  |  |  |         while path != parent: | 
					
						
							|  |  |  |             parents.append(self.with_segments(parent)) | 
					
						
							|  |  |  |             path = parent | 
					
						
							| 
									
										
										
										
											2024-01-14 21:49:53 +00:00
										 |  |  |             parent = split(path)[0] | 
					
						
							| 
									
										
										
										
											2024-01-06 21:17:51 +00:00
										 |  |  |         return tuple(parents) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 01:12:46 +00:00
										 |  |  |     def full_match(self, pattern, *, case_sensitive=None): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Return True if this path matches the given glob-style pattern. The | 
					
						
							|  |  |  |         pattern is matched against the entire path. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
											
												GH-128520: More consistent type-checking behaviour in pathlib (#130199)
In the following methods, skip casting of the argument to a path object if
the argument has a `with_segments` attribute. In `PurePath`:
`relative_to()`, `is_relative_to()`, `match()`, and `full_match()`. In
`Path`: `rename()`, `replace()`, `copy()`, `copy_into()`, `move()`, and
`move_into()`.
Previously the check varied a bit from method to method. The `PurePath`
methods used `isinstance(arg, PurePath)`; the `rename()` and `replace()`
methods always cast, and the remaining `Path` methods checked for a private
`_copy_writer` attribute.
We apply identical changes to relevant methods of the private ABCs. This
improves performance a bit, because `isinstance()` checks on ABCs are
expensive.
											
										 
											2025-02-21 17:47:45 +00:00
										 |  |  |         if not hasattr(pattern, 'with_segments'): | 
					
						
							| 
									
										
										
										
											2024-01-26 01:12:46 +00:00
										 |  |  |             pattern = self.with_segments(pattern) | 
					
						
							|  |  |  |         if case_sensitive is None: | 
					
						
							| 
									
										
										
										
											2025-02-16 17:08:55 +00:00
										 |  |  |             case_sensitive = self.parser.normcase('Aa') == 'Aa' | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |         globber = _PathGlobber(pattern.parser.sep, case_sensitive, recursive=True) | 
					
						
							| 
									
										
										
										
											2024-12-22 01:41:38 +00:00
										 |  |  |         match = globber.compile(str(pattern)) | 
					
						
							|  |  |  |         return match(str(self)) is not None | 
					
						
							| 
									
										
										
										
											2023-05-30 21:18:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												GH-110109: Add `pathlib._PurePathBase` (#110670)
Add private `pathlib._PurePathBase` class: a private superclass of both `PurePath` and `_PathBase`. Unlike `PurePath`, it does not define any of these special methods: `__fspath__`, `__bytes__`, `__reduce__`, `__hash__`, `__eq__`, `__lt__`, `__le__`, `__gt__`, `__ge__`. Its initializer and path joining methods accept only strings, not os.PathLike objects more broadly.
This is important for supporting *virtual paths*: user subclasses of `_PathBase` that provide access to archive files, FTP servers, etc. In these classes, the above methods should be implemented by users only as appropriate, with due consideration for the hash/equality of any backing objects, such as file objects or sockets.
											
										 
											2023-12-08 17:39:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  | class ReadablePath(JoinablePath): | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     """Abstract base class for readable path objects.
 | 
					
						
							| 
									
										
										
										
											2018-02-19 08:36:32 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     The Path class implements this ABC for local filesystem paths. Users may | 
					
						
							|  |  |  |     create subclasses to implement readable virtual filesystem paths, such as | 
					
						
							|  |  |  |     paths in archive files or on remote storage systems. | 
					
						
							| 
									
										
										
										
											2018-02-19 08:36:32 +09:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-04-07 01:26:37 +01:00
										 |  |  |     __slots__ = () | 
					
						
							| 
									
										
										
										
											2023-09-30 15:45:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |     @property | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     @abstractmethod | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |     def info(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         A PathInfo object that exposes the file type and other file attributes | 
					
						
							|  |  |  |         of this path. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |     def exists(self, *, follow_symlinks=True): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path exists. | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |         This method normally follows symlinks; to check whether a symlink exists, | 
					
						
							|  |  |  |         add the argument follow_symlinks=False. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |         info = self.joinpath().info | 
					
						
							|  |  |  |         return info.exists(follow_symlinks=follow_symlinks) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-26 17:58:17 +01:00
										 |  |  |     def is_dir(self, *, follow_symlinks=True): | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path is a directory. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |         info = self.joinpath().info | 
					
						
							|  |  |  |         return info.is_dir(follow_symlinks=follow_symlinks) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-26 17:58:17 +01:00
										 |  |  |     def is_file(self, *, follow_symlinks=True): | 
					
						
							| 
									
										
										
										
											2015-01-12 21:03:41 +01:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |         Whether this path is a regular file (also True for symlinks pointing | 
					
						
							|  |  |  |         to regular files). | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |         info = self.joinpath().info | 
					
						
							|  |  |  |         return info.is_file(follow_symlinks=follow_symlinks) | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def is_symlink(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path is a symbolic link. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |         info = self.joinpath().info | 
					
						
							|  |  |  |         return info.is_symlink() | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     @abstractmethod | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |     def __open_rb__(self, buffering=-1): | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |         """
 | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |         Open the file pointed to by this path for reading in binary mode and | 
					
						
							|  |  |  |         return a file object, like open(mode='rb'). | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-12 17:39:24 +00:00
										 |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def read_bytes(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Open the file in bytes mode, read it, and close the file. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |         with magic_open(self, mode='rb', buffering=0) as f: | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |             return f.read() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 07:32:38 +09:00
										 |  |  |     def read_text(self, encoding=None, errors=None, newline=None): | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Open the file in text mode, read it, and close the file. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |         with magic_open(self, mode='r', encoding=encoding, errors=errors, newline=newline) as f: | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |             return f.read() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     @abstractmethod | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     def iterdir(self): | 
					
						
							| 
									
										
										
										
											2022-11-10 01:05:07 +03:00
										 |  |  |         """Yield path objects of the directory contents.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The children are yielded in arbitrary order, and the | 
					
						
							|  |  |  |         special entries '.' and '..' are not included. | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-12 17:39:24 +00:00
										 |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 19:51:54 +01:00
										 |  |  |     def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """Iterate over this subtree and yield all existing files (of any
 | 
					
						
							| 
									
										
										
										
											2019-02-11 11:47:09 +01:00
										 |  |  |         kind, including directories) matching the given relative pattern. | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
											
												GH-128520: More consistent type-checking behaviour in pathlib (#130199)
In the following methods, skip casting of the argument to a path object if
the argument has a `with_segments` attribute. In `PurePath`:
`relative_to()`, `is_relative_to()`, `match()`, and `full_match()`. In
`Path`: `rename()`, `replace()`, `copy()`, `copy_into()`, `move()`, and
`move_into()`.
Previously the check varied a bit from method to method. The `PurePath`
methods used `isinstance(arg, PurePath)`; the `rename()` and `replace()`
methods always cast, and the remaining `Path` methods checked for a private
`_copy_writer` attribute.
We apply identical changes to relevant methods of the private ABCs. This
improves performance a bit, because `isinstance()` checks on ABCs are
expensive.
											
										 
											2025-02-21 17:47:45 +00:00
										 |  |  |         if not hasattr(pattern, 'with_segments'): | 
					
						
							| 
									
										
										
										
											2024-01-20 02:10:25 +00:00
										 |  |  |             pattern = self.with_segments(pattern) | 
					
						
							| 
									
										
										
										
											2024-12-22 01:41:38 +00:00
										 |  |  |         anchor, parts = _explode_path(pattern) | 
					
						
							| 
									
										
										
										
											2024-04-10 20:43:07 +01:00
										 |  |  |         if anchor: | 
					
						
							|  |  |  |             raise NotImplementedError("Non-relative patterns are unsupported") | 
					
						
							| 
									
										
										
										
											2025-02-16 17:08:55 +00:00
										 |  |  |         case_sensitive_default = self.parser.normcase('Aa') == 'Aa' | 
					
						
							| 
									
										
										
										
											2024-12-22 01:41:38 +00:00
										 |  |  |         if case_sensitive is None: | 
					
						
							| 
									
										
										
										
											2025-02-16 17:08:55 +00:00
										 |  |  |             case_sensitive = case_sensitive_default | 
					
						
							| 
									
										
										
										
											2024-12-22 01:41:38 +00:00
										 |  |  |             case_pedantic = False | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2025-02-16 17:08:55 +00:00
										 |  |  |             case_pedantic = case_sensitive_default != case_sensitive | 
					
						
							| 
									
										
										
										
											2024-12-22 01:41:38 +00:00
										 |  |  |         recursive = True if recurse_symlinks else _no_recurse_symlinks | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |         globber = _PathGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive) | 
					
						
							| 
									
										
										
										
											2024-12-22 01:41:38 +00:00
										 |  |  |         select = globber.selector(parts) | 
					
						
							| 
									
										
										
										
											2025-02-08 06:47:09 +00:00
										 |  |  |         return select(self.joinpath('')) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  |     def walk(self, top_down=True, on_error=None, follow_symlinks=False): | 
					
						
							|  |  |  |         """Walk the directory tree from this directory, similar to os.walk().""" | 
					
						
							| 
									
										
										
										
											2024-05-29 21:51:04 +01:00
										 |  |  |         paths = [self] | 
					
						
							|  |  |  |         while paths: | 
					
						
							|  |  |  |             path = paths.pop() | 
					
						
							|  |  |  |             if isinstance(path, tuple): | 
					
						
							|  |  |  |                 yield path | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             dirnames = [] | 
					
						
							|  |  |  |             filenames = [] | 
					
						
							|  |  |  |             if not top_down: | 
					
						
							|  |  |  |                 paths.append((path, dirnames, filenames)) | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |                 for child in path.iterdir(): | 
					
						
							| 
									
										
										
										
											2025-02-24 19:07:54 +00:00
										 |  |  |                     if child.info.is_dir(follow_symlinks=follow_symlinks): | 
					
						
							|  |  |  |                         if not top_down: | 
					
						
							|  |  |  |                             paths.append(child) | 
					
						
							|  |  |  |                         dirnames.append(child.name) | 
					
						
							|  |  |  |                     else: | 
					
						
							| 
									
										
										
										
											2025-02-08 01:16:45 +00:00
										 |  |  |                         filenames.append(child.name) | 
					
						
							| 
									
										
										
										
											2024-05-29 21:51:04 +01:00
										 |  |  |             except OSError as error: | 
					
						
							|  |  |  |                 if on_error is not None: | 
					
						
							|  |  |  |                     on_error(error) | 
					
						
							|  |  |  |                 if not top_down: | 
					
						
							|  |  |  |                     while not isinstance(paths.pop(), tuple): | 
					
						
							|  |  |  |                         pass | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             if top_down: | 
					
						
							|  |  |  |                 yield path, dirnames, filenames | 
					
						
							|  |  |  |                 paths += [path.joinpath(d) for d in reversed(dirnames)] | 
					
						
							| 
									
										
										
										
											2023-05-07 20:07:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     @abstractmethod | 
					
						
							| 
									
										
										
										
											2023-09-30 15:45:01 +01:00
										 |  |  |     def readlink(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Return the path to which the symbolic link points. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-12 17:39:24 +00:00
										 |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2023-09-30 15:45:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |     def copy(self, target, follow_symlinks=True, dirs_exist_ok=False, | 
					
						
							|  |  |  |              preserve_metadata=False): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Recursively copy this file or directory tree to the given destination. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
											
												GH-128520: More consistent type-checking behaviour in pathlib (#130199)
In the following methods, skip casting of the argument to a path object if
the argument has a `with_segments` attribute. In `PurePath`:
`relative_to()`, `is_relative_to()`, `match()`, and `full_match()`. In
`Path`: `rename()`, `replace()`, `copy()`, `copy_into()`, `move()`, and
`move_into()`.
Previously the check varied a bit from method to method. The `PurePath`
methods used `isinstance(arg, PurePath)`; the `rename()` and `replace()`
methods always cast, and the remaining `Path` methods checked for a private
`_copy_writer` attribute.
We apply identical changes to relevant methods of the private ABCs. This
improves performance a bit, because `isinstance()` checks on ABCs are
expensive.
											
										 
											2025-02-21 17:47:45 +00:00
										 |  |  |         if not hasattr(target, 'with_segments'): | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |             target = self.with_segments(target) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Delegate to the target path's CopyWriter object. | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             create = target._copy_writer._create | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             raise TypeError(f"Target is not writable: {target}") from None | 
					
						
							|  |  |  |         return create(self, follow_symlinks, dirs_exist_ok, preserve_metadata) | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def copy_into(self, target_dir, *, follow_symlinks=True, | 
					
						
							|  |  |  |                   dirs_exist_ok=False, preserve_metadata=False): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Copy this file or directory tree into the given existing directory. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = self.name | 
					
						
							|  |  |  |         if not name: | 
					
						
							|  |  |  |             raise ValueError(f"{self!r} has an empty name") | 
					
						
							| 
									
										
										
											
												GH-128520: More consistent type-checking behaviour in pathlib (#130199)
In the following methods, skip casting of the argument to a path object if
the argument has a `with_segments` attribute. In `PurePath`:
`relative_to()`, `is_relative_to()`, `match()`, and `full_match()`. In
`Path`: `rename()`, `replace()`, `copy()`, `copy_into()`, `move()`, and
`move_into()`.
Previously the check varied a bit from method to method. The `PurePath`
methods used `isinstance(arg, PurePath)`; the `rename()` and `replace()`
methods always cast, and the remaining `Path` methods checked for a private
`_copy_writer` attribute.
We apply identical changes to relevant methods of the private ABCs. This
improves performance a bit, because `isinstance()` checks on ABCs are
expensive.
											
										 
											2025-02-21 17:47:45 +00:00
										 |  |  |         elif hasattr(target_dir, 'with_segments'): | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  |             target = target_dir / name | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             target = self.with_segments(target_dir, name) | 
					
						
							|  |  |  |         return self.copy(target, follow_symlinks=follow_symlinks, | 
					
						
							|  |  |  |                          dirs_exist_ok=dirs_exist_ok, | 
					
						
							|  |  |  |                          preserve_metadata=preserve_metadata) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  | class WritablePath(JoinablePath): | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     """Abstract base class for writable path objects.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The Path class implements this ABC for local filesystem paths. Users may | 
					
						
							|  |  |  |     create subclasses to implement writable virtual filesystem paths, such as | 
					
						
							|  |  |  |     paths in archive files or on remote storage systems. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  |     __slots__ = () | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     @abstractmethod | 
					
						
							| 
									
										
										
										
											2023-09-30 15:45:01 +01:00
										 |  |  |     def symlink_to(self, target, target_is_directory=False): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Make this path a symlink pointing to the target path. | 
					
						
							|  |  |  |         Note the order of arguments (link, target) is the reverse of os.symlink. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-12 17:39:24 +00:00
										 |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2023-09-30 15:45:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     @abstractmethod | 
					
						
							| 
									
										
										
										
											2023-09-30 15:45:01 +01:00
										 |  |  |     def mkdir(self, mode=0o777, parents=False, exist_ok=False): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Create a new directory at this given path. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-12 17:39:24 +00:00
										 |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2023-09-30 15:45:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  |     @abstractmethod | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |     def __open_wb__(self, buffering=-1): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Open the file pointed to by this path for writing in binary mode and | 
					
						
							|  |  |  |         return a file object, like open(mode='wb'). | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  |     def write_bytes(self, data): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Open the file in bytes mode, write to it, and close the file. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # type-check for the buffer interface before truncating the file | 
					
						
							|  |  |  |         view = memoryview(data) | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |         with magic_open(self, mode='wb') as f: | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  |             return f.write(view) | 
					
						
							| 
									
										
										
										
											2024-06-23 22:01:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  |     def write_text(self, data, encoding=None, errors=None, newline=None): | 
					
						
							| 
									
										
										
										
											2024-08-26 14:14:23 +01:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  |         Open the file in text mode, write to it, and close the file. | 
					
						
							| 
									
										
										
										
											2024-08-26 14:14:23 +01:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  |         if not isinstance(data, str): | 
					
						
							|  |  |  |             raise TypeError('data must be str, not %s' % | 
					
						
							|  |  |  |                             data.__class__.__name__) | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |         with magic_open(self, mode='w', encoding=encoding, errors=errors, newline=newline) as f: | 
					
						
							| 
									
										
										
										
											2025-01-11 19:27:47 +00:00
										 |  |  |             return f.write(data) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												GH-128520: Make `pathlib._abc.WritablePath` a sibling of `ReadablePath` (#129014)
In the private pathlib ABCs, support write-only virtual filesystems by
making `WritablePath` inherit directly from `JoinablePath`, rather than
subclassing `ReadablePath`.
There are two complications:
- `ReadablePath.open()` applies to both reading and writing
- `ReadablePath.copy` is secretly an object that supports the *read* side
  of copying, whereas `WritablePath.copy` is a different kind of object
  supporting the *write* side
We untangle these as follow:
- A new `pathlib._abc.magic_open()` function replaces the `open()` method,
  which is dropped from the ABCs but remains in `pathlib.Path`. The
  function works like `io.open()`, but additionally accepts objects with
  `__open_rb__()` or `__open_wb__()` methods as appropriate for the mode.
  These new dunders are made abstract methods of `ReadablePath` and
  `WritablePath` respectively.  If the pathlib ABCs are made public, we
  could consider blessing an "openable" protocol and supporting it in
  `io.open()`, removing the need for `pathlib._abc.magic_open()`.
- `ReadablePath.copy` becomes a true method, whereas `WritablePath.copy` is
  deleted. A new `ReadablePath._copy_reader` property provides a
  `CopyReader` object, and similarly `WritablePath._copy_writer` is a
  `CopyWriter` object. Once GH-125413 is resolved, we'll be able to move
  the `CopyReader` functionality into `ReadablePath.info` and eliminate
  `ReadablePath._copy_reader`.
											
										 
											2025-01-21 18:35:37 +00:00
										 |  |  |     _copy_writer = property(CopyWriter) | 
					
						
							| 
									
										
										
										
											2025-02-16 00:37:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | JoinablePath.register(PurePath) | 
					
						
							|  |  |  | ReadablePath.register(Path) | 
					
						
							|  |  |  | WritablePath.register(Path) |