@@ -1251,6 +1251,52 @@ def _chflags_raiser(path, flags, *, follow_symlinks=True):
12511251 finally :
12521252 os .chflags = old_chflags
12531253
1254+ def test_copystat_handles_utime_errors (self ):
1255+ # gh-42948: copystat should ignore permission errors when setting times
1256+ tmpdir = self .mkdtemp ()
1257+ file1 = os .path .join (tmpdir , 'file1' )
1258+ file2 = os .path .join (tmpdir , 'file2' )
1259+ create_file (file1 , 'xxx' )
1260+ create_file (file2 , 'xxx' )
1261+
1262+ def make_utime_raiser (err ):
1263+ def _utime_raiser (path , times = None , * , ns = None , dir_fd = None ,
1264+ follow_symlinks = True ):
1265+ ex = OSError ()
1266+ ex .errno = err
1267+ raise ex
1268+ return _utime_raiser
1269+
1270+ for err in errno .EPERM , errno .EACCES :
1271+ with unittest .mock .patch ('os.utime' , side_effect = make_utime_raiser (err )):
1272+ shutil .copystat (file1 , file2 )
1273+
1274+ with unittest .mock .patch ('os.utime' , side_effect = make_utime_raiser (errno .EINVAL )):
1275+ self .assertRaises (OSError , shutil .copystat , file1 , file2 )
1276+
1277+ @mock_rename
1278+ def test_move_handles_utime_errors (self ):
1279+ # gh-42948: move should succeed despite utime permission errors
1280+ src_dir = self .mkdtemp ()
1281+ dst_dir = self .mkdtemp ()
1282+ src_file = os .path .join (src_dir , 'file' )
1283+ dst_file = os .path .join (dst_dir , 'file' )
1284+ create_file (src_file , 'content' )
1285+
1286+ def _utime_raiser (path , times = None , * , ns = None , dir_fd = None ,
1287+ follow_symlinks = True ):
1288+ ex = OSError ()
1289+ ex .errno = errno .EPERM
1290+ raise ex
1291+
1292+ with unittest .mock .patch ('os.utime' , side_effect = _utime_raiser ):
1293+ result = shutil .move (src_file , dst_file )
1294+ self .assertEqual (result , dst_file )
1295+ self .assertTrue (os .path .exists (dst_file ))
1296+ self .assertFalse (os .path .exists (src_file ))
1297+ with open (dst_file ) as f :
1298+ self .assertEqual (f .read (), 'content' )
1299+
12541300 ### shutil.copyxattr
12551301
12561302 @os_helper .skip_unless_xattr
0 commit comments