@@ -2467,12 +2467,8 @@ def test_filesystem_full(self):
24672467 self .assertRaises (OSError , self .zerocopy_fun , src , dst )
24682468
24692469
2470- @unittest .skipIf (not SUPPORTS_SENDFILE , 'os.sendfile() not supported' )
2471- class TestZeroCopySendfile (_ZeroCopyFileTest , unittest .TestCase ):
2472- PATCHPOINT = "os.sendfile"
2473-
2474- def zerocopy_fun (self , fsrc , fdst ):
2475- return shutil ._fastcopy_sendfile (fsrc , fdst )
2470+ class _ZeroCopyFileLinuxTest (_ZeroCopyFileTest ):
2471+ BLOCKSIZE_INDEX = None
24762472
24772473 def test_non_regular_file_src (self ):
24782474 with io .BytesIO (self .FILEDATA ) as src :
@@ -2493,65 +2489,65 @@ def test_non_regular_file_dst(self):
24932489 self .assertEqual (dst .read (), self .FILEDATA )
24942490
24952491 def test_exception_on_second_call (self ):
2496- def sendfile (* args , ** kwargs ):
2492+ def syscall (* args , ** kwargs ):
24972493 if not flag :
24982494 flag .append (None )
2499- return orig_sendfile (* args , ** kwargs )
2495+ return orig_syscall (* args , ** kwargs )
25002496 else :
25012497 raise OSError (errno .EBADF , "yo" )
25022498
25032499 flag = []
2504- orig_sendfile = os . sendfile
2505- with unittest .mock .patch ('os.sendfile' , create = True ,
2506- side_effect = sendfile ):
2500+ orig_syscall = eval ( self . PATCHPOINT )
2501+ with unittest .mock .patch (self . PATCHPOINT , create = True ,
2502+ side_effect = syscall ):
25072503 with self .get_files () as (src , dst ):
25082504 with self .assertRaises (OSError ) as cm :
2509- shutil . _fastcopy_sendfile (src , dst )
2505+ self . zerocopy_fun (src , dst )
25102506 assert flag
25112507 self .assertEqual (cm .exception .errno , errno .EBADF )
25122508
25132509 def test_cant_get_size (self ):
25142510 # Emulate a case where src file size cannot be determined.
25152511 # Internally bufsize will be set to a small value and
2516- # sendfile() will be called repeatedly.
2512+ # a system call will be called repeatedly.
25172513 with unittest .mock .patch ('os.fstat' , side_effect = OSError ) as m :
25182514 with self .get_files () as (src , dst ):
2519- shutil . _fastcopy_sendfile (src , dst )
2515+ self . zerocopy_fun (src , dst )
25202516 assert m .called
25212517 self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
25222518
25232519 def test_small_chunks (self ):
25242520 # Force internal file size detection to be smaller than the
2525- # actual file size. We want to force sendfile() to be called
2521+ # actual file size. We want to force a system call to be called
25262522 # multiple times, also in order to emulate a src fd which gets
25272523 # bigger while it is being copied.
25282524 mock = unittest .mock .Mock ()
25292525 mock .st_size = 65536 + 1
25302526 with unittest .mock .patch ('os.fstat' , return_value = mock ) as m :
25312527 with self .get_files () as (src , dst ):
2532- shutil . _fastcopy_sendfile (src , dst )
2528+ self . zerocopy_fun (src , dst )
25332529 assert m .called
25342530 self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
25352531
25362532 def test_big_chunk (self ):
25372533 # Force internal file size detection to be +100MB bigger than
2538- # the actual file size. Make sure sendfile() does not rely on
2534+ # the actual file size. Make sure a system call does not rely on
25392535 # file size value except for (maybe) a better throughput /
25402536 # performance.
25412537 mock = unittest .mock .Mock ()
25422538 mock .st_size = self .FILESIZE + (100 * 1024 * 1024 )
25432539 with unittest .mock .patch ('os.fstat' , return_value = mock ) as m :
25442540 with self .get_files () as (src , dst ):
2545- shutil . _fastcopy_sendfile (src , dst )
2541+ self . zerocopy_fun (src , dst )
25462542 assert m .called
25472543 self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
25482544
25492545 def test_blocksize_arg (self ):
2550- with unittest .mock .patch ('os.sendfile' ,
2546+ with unittest .mock .patch (self . PATCHPOINT ,
25512547 side_effect = ZeroDivisionError ) as m :
25522548 self .assertRaises (ZeroDivisionError ,
25532549 shutil .copyfile , TESTFN , TESTFN2 )
2554- blocksize = m .call_args [0 ][3 ]
2550+ blocksize = m .call_args [0 ][self . BLOCKSIZE_INDEX ]
25552551 # Make sure file size and the block size arg passed to
25562552 # sendfile() are the same.
25572553 self .assertEqual (blocksize , os .path .getsize (TESTFN ))
@@ -2561,9 +2557,19 @@ def test_blocksize_arg(self):
25612557 self .addCleanup (os_helper .unlink , TESTFN2 + '3' )
25622558 self .assertRaises (ZeroDivisionError ,
25632559 shutil .copyfile , TESTFN2 , TESTFN2 + '3' )
2564- blocksize = m .call_args [0 ][3 ]
2560+ blocksize = m .call_args [0 ][self . BLOCKSIZE_INDEX ]
25652561 self .assertEqual (blocksize , 2 ** 23 )
25662562
2563+
2564+ @unittest .skipIf (not SUPPORTS_SENDFILE , 'os.sendfile() not supported' )
2565+ @unittest .mock .patch .object (shutil , "_USE_CP_COPY_FILE_RANGE" , False )
2566+ class TestZeroCopySendfile (_ZeroCopyFileLinuxTest , unittest .TestCase ):
2567+ PATCHPOINT = "os.sendfile"
2568+ BLOCKSIZE_INDEX = 3
2569+
2570+ def zerocopy_fun (self , fsrc , fdst ):
2571+ return shutil ._fastcopy_sendfile (fsrc , fdst )
2572+
25672573 def test_file2file_not_supported (self ):
25682574 # Emulate a case where sendfile() only support file->socket
25692575 # fds. In such a case copyfile() is supposed to skip the
@@ -2586,6 +2592,29 @@ def test_file2file_not_supported(self):
25862592 shutil ._USE_CP_SENDFILE = True
25872593
25882594
2595+ @unittest .skipUnless (shutil ._USE_CP_COPY_FILE_RANGE , "os.copy_file_range() not supported" )
2596+ class TestZeroCopyCopyFileRange (_ZeroCopyFileLinuxTest , unittest .TestCase ):
2597+ PATCHPOINT = "os.copy_file_range"
2598+ BLOCKSIZE_INDEX = 2
2599+
2600+ def zerocopy_fun (self , fsrc , fdst ):
2601+ return shutil ._fastcopy_copy_file_range (fsrc , fdst )
2602+
2603+ def test_empty_file (self ):
2604+ srcname = f"{ TESTFN } src"
2605+ dstname = f"{ TESTFN } dst"
2606+ self .addCleanup (lambda : os_helper .unlink (srcname ))
2607+ self .addCleanup (lambda : os_helper .unlink (dstname ))
2608+ with open (srcname , "wb" ):
2609+ pass
2610+
2611+ with open (srcname , "rb" ) as src , open (dstname , "wb" ) as dst :
2612+ # _fastcopy_copy_file_range gives up copying empty files due
2613+ # to a bug in older Linux.
2614+ with self .assertRaises (shutil ._GiveupOnFastCopy ):
2615+ self .zerocopy_fun (src , dst )
2616+
2617+
25892618@unittest .skipIf (not MACOS , 'macOS only' )
25902619class TestZeroCopyMACOS (_ZeroCopyFileTest , unittest .TestCase ):
25912620 PATCHPOINT = "posix._fcopyfile"
0 commit comments