mirror of
https://github.com/python/cpython.git
synced 2025-07-23 03:05:38 +00:00
GH-73991: Add pathlib.Path.copy()
(#119058)
Add a `Path.copy()` method that copies the content of one file to another. This method is similar to `shutil.copyfile()` but differs in the following ways: - Uses `fcntl.FICLONE` where available (see GH-81338) - Uses `os.copy_file_range` where available (see GH-81340) - Uses `_winapi.CopyFile2` where available, even though this copies more metadata than the other implementations. This makes `WindowsPath.copy()` more similar to `shutil.copy2()`. The method is presently _less_ specified than the `shutil` functions to allow OS-specific optimizations that might copy more or less metadata. Incorporates code from GH-81338 and GH-93152. Co-authored-by: Eryk Sun <eryksun@gmail.com>
This commit is contained in:
parent
2bacc2343c
commit
7c38097add
7 changed files with 271 additions and 2 deletions
|
@ -16,6 +16,7 @@ import operator
|
|||
import posixpath
|
||||
from glob import _GlobberBase, _no_recurse_symlinks
|
||||
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
|
||||
from ._os import copyfileobj
|
||||
|
||||
|
||||
__all__ = ["UnsupportedOperation"]
|
||||
|
@ -563,6 +564,15 @@ class PathBase(PurePathBase):
|
|||
return (st.st_ino == other_st.st_ino and
|
||||
st.st_dev == other_st.st_dev)
|
||||
|
||||
def _samefile_safe(self, other_path):
|
||||
"""
|
||||
Like samefile(), but returns False rather than raising OSError.
|
||||
"""
|
||||
try:
|
||||
return self.samefile(other_path)
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
|
||||
def open(self, mode='r', buffering=-1, encoding=None,
|
||||
errors=None, newline=None):
|
||||
"""
|
||||
|
@ -780,6 +790,26 @@ class PathBase(PurePathBase):
|
|||
"""
|
||||
raise UnsupportedOperation(self._unsupported_msg('mkdir()'))
|
||||
|
||||
def copy(self, target):
|
||||
"""
|
||||
Copy the contents of this file to the given target.
|
||||
"""
|
||||
if not isinstance(target, PathBase):
|
||||
target = self.with_segments(target)
|
||||
if self._samefile_safe(target):
|
||||
raise OSError(f"{self!r} and {target!r} are the same file")
|
||||
with self.open('rb') as source_f:
|
||||
try:
|
||||
with target.open('wb') as target_f:
|
||||
copyfileobj(source_f, target_f)
|
||||
except IsADirectoryError as e:
|
||||
if not target.exists():
|
||||
# Raise a less confusing exception.
|
||||
raise FileNotFoundError(
|
||||
f'Directory does not exist: {target}') from e
|
||||
else:
|
||||
raise
|
||||
|
||||
def rename(self, target):
|
||||
"""
|
||||
Rename this path to the target path.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue