@@ -3239,12 +3239,8 @@ def test_filesystem_full(self):
32393239 self .assertRaises (OSError , self .zerocopy_fun , src , dst )
32403240
32413241
3242- @unittest .skipIf (not SUPPORTS_SENDFILE , 'os.sendfile() not supported' )
3243- class TestZeroCopySendfile (_ZeroCopyFileTest , unittest .TestCase ):
3244- PATCHPOINT = "os.sendfile"
3245-
3246- def zerocopy_fun (self , fsrc , fdst ):
3247- return shutil ._fastcopy_sendfile (fsrc , fdst )
3242+ class _ZeroCopyFileLinuxTest (_ZeroCopyFileTest ):
3243+ BLOCKSIZE_INDEX = None
32483244
32493245 def test_non_regular_file_src (self ):
32503246 with io .BytesIO (self .FILEDATA ) as src :
@@ -3265,65 +3261,65 @@ def test_non_regular_file_dst(self):
32653261 self .assertEqual (dst .read (), self .FILEDATA )
32663262
32673263 def test_exception_on_second_call (self ):
3268- def sendfile (* args , ** kwargs ):
3264+ def syscall (* args , ** kwargs ):
32693265 if not flag :
32703266 flag .append (None )
3271- return orig_sendfile (* args , ** kwargs )
3267+ return orig_syscall (* args , ** kwargs )
32723268 else :
32733269 raise OSError (errno .EBADF , "yo" )
32743270
32753271 flag = []
3276- orig_sendfile = os . sendfile
3277- with unittest .mock .patch ('os.sendfile' , create = True ,
3278- side_effect = sendfile ):
3272+ orig_syscall = eval ( self . PATCHPOINT )
3273+ with unittest .mock .patch (self . PATCHPOINT , create = True ,
3274+ side_effect = syscall ):
32793275 with self .get_files () as (src , dst ):
32803276 with self .assertRaises (OSError ) as cm :
3281- shutil . _fastcopy_sendfile (src , dst )
3277+ self . zerocopy_fun (src , dst )
32823278 assert flag
32833279 self .assertEqual (cm .exception .errno , errno .EBADF )
32843280
32853281 def test_cant_get_size (self ):
32863282 # Emulate a case where src file size cannot be determined.
32873283 # Internally bufsize will be set to a small value and
3288- # sendfile() will be called repeatedly.
3284+ # a system call will be called repeatedly.
32893285 with unittest .mock .patch ('os.fstat' , side_effect = OSError ) as m :
32903286 with self .get_files () as (src , dst ):
3291- shutil . _fastcopy_sendfile (src , dst )
3287+ self . zerocopy_fun (src , dst )
32923288 assert m .called
32933289 self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
32943290
32953291 def test_small_chunks (self ):
32963292 # Force internal file size detection to be smaller than the
3297- # actual file size. We want to force sendfile() to be called
3293+ # actual file size. We want to force a system call to be called
32983294 # multiple times, also in order to emulate a src fd which gets
32993295 # bigger while it is being copied.
33003296 mock = unittest .mock .Mock ()
33013297 mock .st_size = 65536 + 1
33023298 with unittest .mock .patch ('os.fstat' , return_value = mock ) as m :
33033299 with self .get_files () as (src , dst ):
3304- shutil . _fastcopy_sendfile (src , dst )
3300+ self . zerocopy_fun (src , dst )
33053301 assert m .called
33063302 self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
33073303
33083304 def test_big_chunk (self ):
33093305 # Force internal file size detection to be +100MB bigger than
3310- # the actual file size. Make sure sendfile() does not rely on
3306+ # the actual file size. Make sure a system call does not rely on
33113307 # file size value except for (maybe) a better throughput /
33123308 # performance.
33133309 mock = unittest .mock .Mock ()
33143310 mock .st_size = self .FILESIZE + (100 * 1024 * 1024 )
33153311 with unittest .mock .patch ('os.fstat' , return_value = mock ) as m :
33163312 with self .get_files () as (src , dst ):
3317- shutil . _fastcopy_sendfile (src , dst )
3313+ self . zerocopy_fun (src , dst )
33183314 assert m .called
33193315 self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
33203316
33213317 def test_blocksize_arg (self ):
3322- with unittest .mock .patch ('os.sendfile' ,
3318+ with unittest .mock .patch (self . PATCHPOINT ,
33233319 side_effect = ZeroDivisionError ) as m :
33243320 self .assertRaises (ZeroDivisionError ,
33253321 shutil .copyfile , TESTFN , TESTFN2 )
3326- blocksize = m .call_args [0 ][3 ]
3322+ blocksize = m .call_args [0 ][self . BLOCKSIZE_INDEX ]
33273323 # Make sure file size and the block size arg passed to
33283324 # sendfile() are the same.
33293325 self .assertEqual (blocksize , os .path .getsize (TESTFN ))
@@ -3333,9 +3329,19 @@ def test_blocksize_arg(self):
33333329 self .addCleanup (os_helper .unlink , TESTFN2 + '3' )
33343330 self .assertRaises (ZeroDivisionError ,
33353331 shutil .copyfile , TESTFN2 , TESTFN2 + '3' )
3336- blocksize = m .call_args [0 ][3 ]
3332+ blocksize = m .call_args [0 ][self . BLOCKSIZE_INDEX ]
33373333 self .assertEqual (blocksize , 2 ** 23 )
33383334
3335+
3336+ @unittest .skipIf (not SUPPORTS_SENDFILE , 'os.sendfile() not supported' )
3337+ @unittest .mock .patch .object (shutil , "_USE_CP_COPY_FILE_RANGE" , False )
3338+ class TestZeroCopySendfile (_ZeroCopyFileLinuxTest , unittest .TestCase ):
3339+ PATCHPOINT = "os.sendfile"
3340+ BLOCKSIZE_INDEX = 3
3341+
3342+ def zerocopy_fun (self , fsrc , fdst ):
3343+ return shutil ._fastcopy_sendfile (fsrc , fdst )
3344+
33393345 def test_file2file_not_supported (self ):
33403346 # Emulate a case where sendfile() only support file->socket
33413347 # fds. In such a case copyfile() is supposed to skip the
@@ -3358,6 +3364,29 @@ def test_file2file_not_supported(self):
33583364 shutil ._USE_CP_SENDFILE = True
33593365
33603366
3367+ @unittest .skipUnless (shutil ._USE_CP_COPY_FILE_RANGE , "os.copy_file_range() not supported" )
3368+ class TestZeroCopyCopyFileRange (_ZeroCopyFileLinuxTest , unittest .TestCase ):
3369+ PATCHPOINT = "os.copy_file_range"
3370+ BLOCKSIZE_INDEX = 2
3371+
3372+ def zerocopy_fun (self , fsrc , fdst ):
3373+ return shutil ._fastcopy_copy_file_range (fsrc , fdst )
3374+
3375+ def test_empty_file (self ):
3376+ srcname = f"{ TESTFN } src"
3377+ dstname = f"{ TESTFN } dst"
3378+ self .addCleanup (lambda : os_helper .unlink (srcname ))
3379+ self .addCleanup (lambda : os_helper .unlink (dstname ))
3380+ with open (srcname , "wb" ):
3381+ pass
3382+
3383+ with open (srcname , "rb" ) as src , open (dstname , "wb" ) as dst :
3384+ # _fastcopy_copy_file_range gives up copying empty files due
3385+ # to a bug in older Linux.
3386+ with self .assertRaises (shutil ._GiveupOnFastCopy ):
3387+ self .zerocopy_fun (src , dst )
3388+
3389+
33613390@unittest .skipIf (not MACOS , 'macOS only' )
33623391class TestZeroCopyMACOS (_ZeroCopyFileTest , unittest .TestCase ):
33633392 PATCHPOINT = "posix._fcopyfile"
0 commit comments