@@ -3239,12 +3239,8 @@ def test_filesystem_full(self):
3239
3239
self .assertRaises (OSError , self .zerocopy_fun , src , dst )
3240
3240
3241
3241
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
3248
3244
3249
3245
def test_non_regular_file_src (self ):
3250
3246
with io .BytesIO (self .FILEDATA ) as src :
@@ -3265,65 +3261,65 @@ def test_non_regular_file_dst(self):
3265
3261
self .assertEqual (dst .read (), self .FILEDATA )
3266
3262
3267
3263
def test_exception_on_second_call (self ):
3268
- def sendfile (* args , ** kwargs ):
3264
+ def syscall (* args , ** kwargs ):
3269
3265
if not flag :
3270
3266
flag .append (None )
3271
- return orig_sendfile (* args , ** kwargs )
3267
+ return orig_syscall (* args , ** kwargs )
3272
3268
else :
3273
3269
raise OSError (errno .EBADF , "yo" )
3274
3270
3275
3271
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 ):
3279
3275
with self .get_files () as (src , dst ):
3280
3276
with self .assertRaises (OSError ) as cm :
3281
- shutil . _fastcopy_sendfile (src , dst )
3277
+ self . zerocopy_fun (src , dst )
3282
3278
assert flag
3283
3279
self .assertEqual (cm .exception .errno , errno .EBADF )
3284
3280
3285
3281
def test_cant_get_size (self ):
3286
3282
# Emulate a case where src file size cannot be determined.
3287
3283
# Internally bufsize will be set to a small value and
3288
- # sendfile() will be called repeatedly.
3284
+ # a system call will be called repeatedly.
3289
3285
with unittest .mock .patch ('os.fstat' , side_effect = OSError ) as m :
3290
3286
with self .get_files () as (src , dst ):
3291
- shutil . _fastcopy_sendfile (src , dst )
3287
+ self . zerocopy_fun (src , dst )
3292
3288
assert m .called
3293
3289
self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
3294
3290
3295
3291
def test_small_chunks (self ):
3296
3292
# 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
3298
3294
# multiple times, also in order to emulate a src fd which gets
3299
3295
# bigger while it is being copied.
3300
3296
mock = unittest .mock .Mock ()
3301
3297
mock .st_size = 65536 + 1
3302
3298
with unittest .mock .patch ('os.fstat' , return_value = mock ) as m :
3303
3299
with self .get_files () as (src , dst ):
3304
- shutil . _fastcopy_sendfile (src , dst )
3300
+ self . zerocopy_fun (src , dst )
3305
3301
assert m .called
3306
3302
self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
3307
3303
3308
3304
def test_big_chunk (self ):
3309
3305
# 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
3311
3307
# file size value except for (maybe) a better throughput /
3312
3308
# performance.
3313
3309
mock = unittest .mock .Mock ()
3314
3310
mock .st_size = self .FILESIZE + (100 * 1024 * 1024 )
3315
3311
with unittest .mock .patch ('os.fstat' , return_value = mock ) as m :
3316
3312
with self .get_files () as (src , dst ):
3317
- shutil . _fastcopy_sendfile (src , dst )
3313
+ self . zerocopy_fun (src , dst )
3318
3314
assert m .called
3319
3315
self .assertEqual (read_file (TESTFN2 , binary = True ), self .FILEDATA )
3320
3316
3321
3317
def test_blocksize_arg (self ):
3322
- with unittest .mock .patch ('os.sendfile' ,
3318
+ with unittest .mock .patch (self . PATCHPOINT ,
3323
3319
side_effect = ZeroDivisionError ) as m :
3324
3320
self .assertRaises (ZeroDivisionError ,
3325
3321
shutil .copyfile , TESTFN , TESTFN2 )
3326
- blocksize = m .call_args [0 ][3 ]
3322
+ blocksize = m .call_args [0 ][self . BLOCKSIZE_INDEX ]
3327
3323
# Make sure file size and the block size arg passed to
3328
3324
# sendfile() are the same.
3329
3325
self .assertEqual (blocksize , os .path .getsize (TESTFN ))
@@ -3333,9 +3329,19 @@ def test_blocksize_arg(self):
3333
3329
self .addCleanup (os_helper .unlink , TESTFN2 + '3' )
3334
3330
self .assertRaises (ZeroDivisionError ,
3335
3331
shutil .copyfile , TESTFN2 , TESTFN2 + '3' )
3336
- blocksize = m .call_args [0 ][3 ]
3332
+ blocksize = m .call_args [0 ][self . BLOCKSIZE_INDEX ]
3337
3333
self .assertEqual (blocksize , 2 ** 23 )
3338
3334
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
+
3339
3345
def test_file2file_not_supported (self ):
3340
3346
# Emulate a case where sendfile() only support file->socket
3341
3347
# fds. In such a case copyfile() is supposed to skip the
@@ -3358,6 +3364,29 @@ def test_file2file_not_supported(self):
3358
3364
shutil ._USE_CP_SENDFILE = True
3359
3365
3360
3366
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
+
3361
3390
@unittest .skipIf (not MACOS , 'macOS only' )
3362
3391
class TestZeroCopyMACOS (_ZeroCopyFileTest , unittest .TestCase ):
3363
3392
PATCHPOINT = "posix._fcopyfile"
0 commit comments