@@ -2467,12 +2467,8 @@ def test_filesystem_full(self):
2467
2467
self .assertRaises (OSError , self .zerocopy_fun , src , dst )
2468
2468
2469
2469
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
2476
2472
2477
2473
def test_non_regular_file_src (self ):
2478
2474
with io .BytesIO (self .FILEDATA ) as src :
@@ -2493,65 +2489,65 @@ def test_non_regular_file_dst(self):
2493
2489
self .assertEqual (dst .read (), self .FILEDATA )
2494
2490
2495
2491
def test_exception_on_second_call (self ):
2496
- def sendfile (* args , ** kwargs ):
2492
+ def syscall (* args , ** kwargs ):
2497
2493
if not flag :
2498
2494
flag .append (None )
2499
- return orig_sendfile (* args , ** kwargs )
2495
+ return orig_syscall (* args , ** kwargs )
2500
2496
else :
2501
2497
raise OSError (errno .EBADF , "yo" )
2502
2498
2503
2499
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 ):
2507
2503
with self .get_files () as (src , dst ):
2508
2504
with self .assertRaises (OSError ) as cm :
2509
- shutil . _fastcopy_sendfile (src , dst )
2505
+ self . zerocopy_fun (src , dst )
2510
2506
assert flag
2511
2507
self .assertEqual (cm .exception .errno , errno .EBADF )
2512
2508
2513
2509
def test_cant_get_size (self ):
2514
2510
# Emulate a case where src file size cannot be determined.
2515
2511
# Internally bufsize will be set to a small value and
2516
- # sendfile() will be called repeatedly.
2512
+ # a system call will be called repeatedly.
2517
2513
with unittest .mock .patch ('os.fstat' , side_effect = OSError ) as m :
2518
2514
with self .get_files () as (src , dst ):
2519
- shutil . _fastcopy_sendfile (src , dst )
2515
+ self . zerocopy_fun (src , dst )
2520
2516
assert m .called
2521
2517
self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
2522
2518
2523
2519
def test_small_chunks (self ):
2524
2520
# 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
2526
2522
# multiple times, also in order to emulate a src fd which gets
2527
2523
# bigger while it is being copied.
2528
2524
mock = unittest .mock .Mock ()
2529
2525
mock .st_size = 65536 + 1
2530
2526
with unittest .mock .patch ('os.fstat' , return_value = mock ) as m :
2531
2527
with self .get_files () as (src , dst ):
2532
- shutil . _fastcopy_sendfile (src , dst )
2528
+ self . zerocopy_fun (src , dst )
2533
2529
assert m .called
2534
2530
self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
2535
2531
2536
2532
def test_big_chunk (self ):
2537
2533
# 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
2539
2535
# file size value except for (maybe) a better throughput /
2540
2536
# performance.
2541
2537
mock = unittest .mock .Mock ()
2542
2538
mock .st_size = self .FILESIZE + (100 * 1024 * 1024 )
2543
2539
with unittest .mock .patch ('os.fstat' , return_value = mock ) as m :
2544
2540
with self .get_files () as (src , dst ):
2545
- shutil . _fastcopy_sendfile (src , dst )
2541
+ self . zerocopy_fun (src , dst )
2546
2542
assert m .called
2547
2543
self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
2548
2544
2549
2545
def test_blocksize_arg (self ):
2550
- with unittest .mock .patch ('os.sendfile' ,
2546
+ with unittest .mock .patch (self . PATCHPOINT ,
2551
2547
side_effect = ZeroDivisionError ) as m :
2552
2548
self .assertRaises (ZeroDivisionError ,
2553
2549
shutil .copyfile , TESTFN , TESTFN2 )
2554
- blocksize = m .call_args [0 ][3 ]
2550
+ blocksize = m .call_args [0 ][self . BLOCKSIZE_INDEX ]
2555
2551
# Make sure file size and the block size arg passed to
2556
2552
# sendfile() are the same.
2557
2553
self .assertEqual (blocksize , os .path .getsize (TESTFN ))
@@ -2561,9 +2557,19 @@ def test_blocksize_arg(self):
2561
2557
self .addCleanup (os_helper .unlink , TESTFN2 + '3' )
2562
2558
self .assertRaises (ZeroDivisionError ,
2563
2559
shutil .copyfile , TESTFN2 , TESTFN2 + '3' )
2564
- blocksize = m .call_args [0 ][3 ]
2560
+ blocksize = m .call_args [0 ][self . BLOCKSIZE_INDEX ]
2565
2561
self .assertEqual (blocksize , 2 ** 23 )
2566
2562
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
+
2567
2573
def test_file2file_not_supported (self ):
2568
2574
# Emulate a case where sendfile() only support file->socket
2569
2575
# fds. In such a case copyfile() is supposed to skip the
@@ -2586,6 +2592,29 @@ def test_file2file_not_supported(self):
2586
2592
shutil ._USE_CP_SENDFILE = True
2587
2593
2588
2594
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
+
2589
2618
@unittest .skipIf (not MACOS , 'macOS only' )
2590
2619
class TestZeroCopyMACOS (_ZeroCopyFileTest , unittest .TestCase ):
2591
2620
PATCHPOINT = "posix._fcopyfile"
0 commit comments