mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
Merge 53f91b2b23 into 7099af8f5e
This commit is contained in:
commit
b6da2fed95
3 changed files with 56 additions and 2 deletions
|
|
@ -443,8 +443,13 @@ def lookup(name):
|
||||||
else:
|
else:
|
||||||
st = lookup("stat")(src, follow_symlinks=follow)
|
st = lookup("stat")(src, follow_symlinks=follow)
|
||||||
mode = stat.S_IMODE(st.st_mode)
|
mode = stat.S_IMODE(st.st_mode)
|
||||||
lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns),
|
try:
|
||||||
follow_symlinks=follow)
|
lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns),
|
||||||
|
follow_symlinks=follow)
|
||||||
|
except OSError as e:
|
||||||
|
# Ignore permission errors when setting file times (gh-42948).
|
||||||
|
if e.errno not in (errno.EPERM, errno.EACCES):
|
||||||
|
raise
|
||||||
# We must copy extended attributes before the file is (potentially)
|
# We must copy extended attributes before the file is (potentially)
|
||||||
# chmod()'ed read-only, otherwise setxattr() will error with -EACCES.
|
# chmod()'ed read-only, otherwise setxattr() will error with -EACCES.
|
||||||
_copyxattr(src, dst, follow_symlinks=follow)
|
_copyxattr(src, dst, follow_symlinks=follow)
|
||||||
|
|
|
||||||
|
|
@ -1251,6 +1251,52 @@ def _chflags_raiser(path, flags, *, follow_symlinks=True):
|
||||||
finally:
|
finally:
|
||||||
os.chflags = old_chflags
|
os.chflags = old_chflags
|
||||||
|
|
||||||
|
def test_copystat_handles_utime_errors(self):
|
||||||
|
# gh-42948: copystat should ignore permission errors when setting times
|
||||||
|
tmpdir = self.mkdtemp()
|
||||||
|
file1 = os.path.join(tmpdir, 'file1')
|
||||||
|
file2 = os.path.join(tmpdir, 'file2')
|
||||||
|
create_file(file1, 'xxx')
|
||||||
|
create_file(file2, 'xxx')
|
||||||
|
|
||||||
|
def make_utime_raiser(err):
|
||||||
|
def _utime_raiser(path, times=None, *, ns=None, dir_fd=None,
|
||||||
|
follow_symlinks=True):
|
||||||
|
ex = OSError()
|
||||||
|
ex.errno = err
|
||||||
|
raise ex
|
||||||
|
return _utime_raiser
|
||||||
|
|
||||||
|
for err in errno.EPERM, errno.EACCES:
|
||||||
|
with unittest.mock.patch('os.utime', side_effect=make_utime_raiser(err)):
|
||||||
|
shutil.copystat(file1, file2)
|
||||||
|
|
||||||
|
with unittest.mock.patch('os.utime', side_effect=make_utime_raiser(errno.EINVAL)):
|
||||||
|
self.assertRaises(OSError, shutil.copystat, file1, file2)
|
||||||
|
|
||||||
|
@mock_rename
|
||||||
|
def test_move_handles_utime_errors(self):
|
||||||
|
# gh-42948: move should succeed despite utime permission errors
|
||||||
|
src_dir = self.mkdtemp()
|
||||||
|
dst_dir = self.mkdtemp()
|
||||||
|
src_file = os.path.join(src_dir, 'file')
|
||||||
|
dst_file = os.path.join(dst_dir, 'file')
|
||||||
|
create_file(src_file, 'content')
|
||||||
|
|
||||||
|
def _utime_raiser(path, times=None, *, ns=None, dir_fd=None,
|
||||||
|
follow_symlinks=True):
|
||||||
|
ex = OSError()
|
||||||
|
ex.errno = errno.EPERM
|
||||||
|
raise ex
|
||||||
|
|
||||||
|
with unittest.mock.patch('os.utime', side_effect=_utime_raiser):
|
||||||
|
result = shutil.move(src_file, dst_file)
|
||||||
|
self.assertEqual(result, dst_file)
|
||||||
|
self.assertTrue(os.path.exists(dst_file))
|
||||||
|
self.assertFalse(os.path.exists(src_file))
|
||||||
|
with open(dst_file) as f:
|
||||||
|
self.assertEqual(f.read(), 'content')
|
||||||
|
|
||||||
### shutil.copyxattr
|
### shutil.copyxattr
|
||||||
|
|
||||||
@os_helper.skip_unless_xattr
|
@os_helper.skip_unless_xattr
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
:func:`shutil.copystat` now ignores permission errors when setting file
|
||||||
|
times. This fixes :func:`shutil.move` failures on filesystems where the user
|
||||||
|
has write access but is not the owner. Patch by Shamil Abdulaev.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue