2
2
# does add tests for a few functions which have been determined to be more
3
3
# portable than they had been thought to be.
4
4
5
+ import asyncio
5
6
import codecs
6
7
import contextlib
7
8
import decimal
23
24
import sys
24
25
import sysconfig
25
26
import tempfile
26
- import threading
27
27
import time
28
28
import types
29
29
import unittest
33
33
from test .support import import_helper
34
34
from test .support import os_helper
35
35
from test .support import socket_helper
36
- from test .support import threading_helper
37
36
from test .support import warnings_helper
38
37
from platform import win32_is_iot
39
38
40
- with warnings .catch_warnings ():
41
- warnings .simplefilter ('ignore' , DeprecationWarning )
42
- import asynchat
43
- import asyncore
44
-
45
39
try :
46
40
import resource
47
41
except ImportError :
@@ -101,6 +95,10 @@ def create_file(filename, content=b'content'):
101
95
'on AIX, splice() only accepts sockets' )
102
96
103
97
98
+ def tearDownModule ():
99
+ asyncio .set_event_loop_policy (None )
100
+
101
+
104
102
class MiscTests (unittest .TestCase ):
105
103
def test_getcwd (self ):
106
104
cwd = os .getcwd ()
@@ -3228,94 +3226,8 @@ def test_set_get_priority(self):
3228
3226
raise
3229
3227
3230
3228
3231
- class SendfileTestServer (asyncore .dispatcher , threading .Thread ):
3232
-
3233
- class Handler (asynchat .async_chat ):
3234
-
3235
- def __init__ (self , conn ):
3236
- asynchat .async_chat .__init__ (self , conn )
3237
- self .in_buffer = []
3238
- self .accumulate = True
3239
- self .closed = False
3240
- self .push (b"220 ready\r \n " )
3241
-
3242
- def handle_read (self ):
3243
- data = self .recv (4096 )
3244
- if self .accumulate :
3245
- self .in_buffer .append (data )
3246
-
3247
- def get_data (self ):
3248
- return b'' .join (self .in_buffer )
3249
-
3250
- def handle_close (self ):
3251
- self .close ()
3252
- self .closed = True
3253
-
3254
- def handle_error (self ):
3255
- raise
3256
-
3257
- def __init__ (self , address ):
3258
- threading .Thread .__init__ (self )
3259
- asyncore .dispatcher .__init__ (self )
3260
- self .create_socket (socket .AF_INET , socket .SOCK_STREAM )
3261
- self .bind (address )
3262
- self .listen (5 )
3263
- self .host , self .port = self .socket .getsockname ()[:2 ]
3264
- self .handler_instance = None
3265
- self ._active = False
3266
- self ._active_lock = threading .Lock ()
3267
-
3268
- # --- public API
3269
-
3270
- @property
3271
- def running (self ):
3272
- return self ._active
3273
-
3274
- def start (self ):
3275
- assert not self .running
3276
- self .__flag = threading .Event ()
3277
- threading .Thread .start (self )
3278
- self .__flag .wait ()
3279
-
3280
- def stop (self ):
3281
- assert self .running
3282
- self ._active = False
3283
- self .join ()
3284
-
3285
- def wait (self ):
3286
- # wait for handler connection to be closed, then stop the server
3287
- while not getattr (self .handler_instance , "closed" , False ):
3288
- time .sleep (0.001 )
3289
- self .stop ()
3290
-
3291
- # --- internals
3292
-
3293
- def run (self ):
3294
- self ._active = True
3295
- self .__flag .set ()
3296
- while self ._active and asyncore .socket_map :
3297
- self ._active_lock .acquire ()
3298
- asyncore .loop (timeout = 0.001 , count = 1 )
3299
- self ._active_lock .release ()
3300
- asyncore .close_all ()
3301
-
3302
- def handle_accept (self ):
3303
- conn , addr = self .accept ()
3304
- self .handler_instance = self .Handler (conn )
3305
-
3306
- def handle_connect (self ):
3307
- self .close ()
3308
- handle_read = handle_connect
3309
-
3310
- def writable (self ):
3311
- return 0
3312
-
3313
- def handle_error (self ):
3314
- raise
3315
-
3316
-
3317
3229
@unittest .skipUnless (hasattr (os , 'sendfile' ), "test needs os.sendfile()" )
3318
- class TestSendfile (unittest .TestCase ):
3230
+ class TestSendfile (unittest .IsolatedAsyncioTestCase ):
3319
3231
3320
3232
DATA = b"12345abcde" * 16 * 1024 # 160 KiB
3321
3233
SUPPORT_HEADERS_TRAILERS = not sys .platform .startswith ("linux" ) and \
@@ -3328,40 +3240,52 @@ class TestSendfile(unittest.TestCase):
3328
3240
3329
3241
@classmethod
3330
3242
def setUpClass (cls ):
3331
- cls .key = threading_helper .threading_setup ()
3332
3243
create_file (os_helper .TESTFN , cls .DATA )
3333
3244
3334
3245
@classmethod
3335
3246
def tearDownClass (cls ):
3336
- threading_helper .threading_cleanup (* cls .key )
3337
3247
os_helper .unlink (os_helper .TESTFN )
3338
3248
3339
- def setUp (self ):
3340
- self .server = SendfileTestServer ((socket_helper .HOST , 0 ))
3341
- self .server .start ()
3249
+ @staticmethod
3250
+ async def chunks (reader ):
3251
+ while not reader .at_eof ():
3252
+ yield await reader .read ()
3253
+
3254
+ async def handle_new_client (self , reader , writer ):
3255
+ self .server_buffer = b'' .join ([x async for x in self .chunks (reader )])
3256
+ writer .close ()
3257
+ self .server .close () # The test server processes a single client only
3258
+
3259
+ async def asyncSetUp (self ):
3260
+ self .server_buffer = b''
3261
+ self .server = await asyncio .start_server (self .handle_new_client ,
3262
+ socket_helper .HOSTv4 )
3263
+ server_name = self .server .sockets [0 ].getsockname ()
3342
3264
self .client = socket .socket ()
3343
- self .client .connect ((self .server .host , self .server .port ))
3344
- self .client .settimeout (1 )
3345
- # synchronize by waiting for "220 ready" response
3346
- self .client .recv (1024 )
3265
+ self .client .setblocking (False )
3266
+ await asyncio .get_running_loop ().sock_connect (self .client , server_name )
3347
3267
self .sockno = self .client .fileno ()
3348
3268
self .file = open (os_helper .TESTFN , 'rb' )
3349
3269
self .fileno = self .file .fileno ()
3350
3270
3351
- def tearDown (self ):
3271
+ async def asyncTearDown (self ):
3352
3272
self .file .close ()
3353
3273
self .client .close ()
3354
- if self .server .running :
3355
- self .server .stop ()
3356
- self .server = None
3274
+ await self .server .wait_closed ()
3275
+
3276
+ # Use the test subject instead of asyncio.loop.sendfile
3277
+ @staticmethod
3278
+ async def async_sendfile (* args , ** kwargs ):
3279
+ return await asyncio .to_thread (os .sendfile , * args , ** kwargs )
3357
3280
3358
- def sendfile_wrapper (self , * args , ** kwargs ):
3281
+ @staticmethod
3282
+ async def sendfile_wrapper (* args , ** kwargs ):
3359
3283
"""A higher level wrapper representing how an application is
3360
3284
supposed to use sendfile().
3361
3285
"""
3362
3286
while True :
3363
3287
try :
3364
- return os . sendfile (* args , ** kwargs )
3288
+ return await TestSendfile . async_sendfile (* args , ** kwargs )
3365
3289
except OSError as err :
3366
3290
if err .errno == errno .ECONNRESET :
3367
3291
# disconnected
@@ -3372,13 +3296,14 @@ def sendfile_wrapper(self, *args, **kwargs):
3372
3296
else :
3373
3297
raise
3374
3298
3375
- def test_send_whole_file (self ):
3299
+ async def test_send_whole_file (self ):
3376
3300
# normal send
3377
3301
total_sent = 0
3378
3302
offset = 0
3379
3303
nbytes = 4096
3380
3304
while total_sent < len (self .DATA ):
3381
- sent = self .sendfile_wrapper (self .sockno , self .fileno , offset , nbytes )
3305
+ sent = await self .sendfile_wrapper (self .sockno , self .fileno ,
3306
+ offset , nbytes )
3382
3307
if sent == 0 :
3383
3308
break
3384
3309
offset += sent
@@ -3389,19 +3314,19 @@ def test_send_whole_file(self):
3389
3314
self .assertEqual (total_sent , len (self .DATA ))
3390
3315
self .client .shutdown (socket .SHUT_RDWR )
3391
3316
self .client .close ()
3392
- self .server .wait ()
3393
- data = self .server .handler_instance .get_data ()
3394
- self .assertEqual (len (data ), len (self .DATA ))
3395
- self .assertEqual (data , self .DATA )
3317
+ await self .server .wait_closed ()
3318
+ self .assertEqual (len (self .server_buffer ), len (self .DATA ))
3319
+ self .assertEqual (self .server_buffer , self .DATA )
3396
3320
3397
- def test_send_at_certain_offset (self ):
3321
+ async def test_send_at_certain_offset (self ):
3398
3322
# start sending a file at a certain offset
3399
3323
total_sent = 0
3400
3324
offset = len (self .DATA ) // 2
3401
3325
must_send = len (self .DATA ) - offset
3402
3326
nbytes = 4096
3403
3327
while total_sent < must_send :
3404
- sent = self .sendfile_wrapper (self .sockno , self .fileno , offset , nbytes )
3328
+ sent = await self .sendfile_wrapper (self .sockno , self .fileno ,
3329
+ offset , nbytes )
3405
3330
if sent == 0 :
3406
3331
break
3407
3332
offset += sent
@@ -3410,18 +3335,18 @@ def test_send_at_certain_offset(self):
3410
3335
3411
3336
self .client .shutdown (socket .SHUT_RDWR )
3412
3337
self .client .close ()
3413
- self .server .wait ()
3414
- data = self .server .handler_instance .get_data ()
3338
+ await self .server .wait_closed ()
3415
3339
expected = self .DATA [len (self .DATA ) // 2 :]
3416
3340
self .assertEqual (total_sent , len (expected ))
3417
- self .assertEqual (len (data ), len (expected ))
3418
- self .assertEqual (data , expected )
3341
+ self .assertEqual (len (self . server_buffer ), len (expected ))
3342
+ self .assertEqual (self . server_buffer , expected )
3419
3343
3420
- def test_offset_overflow (self ):
3344
+ async def test_offset_overflow (self ):
3421
3345
# specify an offset > file size
3422
3346
offset = len (self .DATA ) + 4096
3423
3347
try :
3424
- sent = os .sendfile (self .sockno , self .fileno , offset , 4096 )
3348
+ sent = await self .async_sendfile (self .sockno , self .fileno ,
3349
+ offset , 4096 )
3425
3350
except OSError as e :
3426
3351
# Solaris can raise EINVAL if offset >= file length, ignore.
3427
3352
if e .errno != errno .EINVAL :
@@ -3430,39 +3355,38 @@ def test_offset_overflow(self):
3430
3355
self .assertEqual (sent , 0 )
3431
3356
self .client .shutdown (socket .SHUT_RDWR )
3432
3357
self .client .close ()
3433
- self .server .wait ()
3434
- data = self .server .handler_instance .get_data ()
3435
- self .assertEqual (data , b'' )
3358
+ await self .server .wait_closed ()
3359
+ self .assertEqual (self .server_buffer , b'' )
3436
3360
3437
- def test_invalid_offset (self ):
3361
+ async def test_invalid_offset (self ):
3438
3362
with self .assertRaises (OSError ) as cm :
3439
- os . sendfile (self .sockno , self .fileno , - 1 , 4096 )
3363
+ await self . async_sendfile (self .sockno , self .fileno , - 1 , 4096 )
3440
3364
self .assertEqual (cm .exception .errno , errno .EINVAL )
3441
3365
3442
- def test_keywords (self ):
3366
+ async def test_keywords (self ):
3443
3367
# Keyword arguments should be supported
3444
- os . sendfile (out_fd = self .sockno , in_fd = self .fileno ,
3445
- offset = 0 , count = 4096 )
3368
+ await self . async_sendfile (out_fd = self .sockno , in_fd = self .fileno ,
3369
+ offset = 0 , count = 4096 )
3446
3370
if self .SUPPORT_HEADERS_TRAILERS :
3447
- os . sendfile (out_fd = self .sockno , in_fd = self .fileno ,
3448
- offset = 0 , count = 4096 ,
3449
- headers = (), trailers = (), flags = 0 )
3371
+ await self . async_sendfile (out_fd = self .sockno , in_fd = self .fileno ,
3372
+ offset = 0 , count = 4096 ,
3373
+ headers = (), trailers = (), flags = 0 )
3450
3374
3451
3375
# --- headers / trailers tests
3452
3376
3453
3377
@requires_headers_trailers
3454
- def test_headers (self ):
3378
+ async def test_headers (self ):
3455
3379
total_sent = 0
3456
3380
expected_data = b"x" * 512 + b"y" * 256 + self .DATA [:- 1 ]
3457
- sent = os . sendfile (self .sockno , self .fileno , 0 , 4096 ,
3458
- headers = [b"x" * 512 , b"y" * 256 ])
3381
+ sent = await self . async_sendfile (self .sockno , self .fileno , 0 , 4096 ,
3382
+ headers = [b"x" * 512 , b"y" * 256 ])
3459
3383
self .assertLessEqual (sent , 512 + 256 + 4096 )
3460
3384
total_sent += sent
3461
3385
offset = 4096
3462
3386
while total_sent < len (expected_data ):
3463
3387
nbytes = min (len (expected_data ) - total_sent , 4096 )
3464
- sent = self .sendfile_wrapper (self .sockno , self .fileno ,
3465
- offset , nbytes )
3388
+ sent = await self .sendfile_wrapper (self .sockno , self .fileno ,
3389
+ offset , nbytes )
3466
3390
if sent == 0 :
3467
3391
break
3468
3392
self .assertLessEqual (sent , nbytes )
@@ -3471,51 +3395,49 @@ def test_headers(self):
3471
3395
3472
3396
self .assertEqual (total_sent , len (expected_data ))
3473
3397
self .client .close ()
3474
- self .server .wait ()
3475
- data = self .server .handler_instance .get_data ()
3476
- self .assertEqual (hash (data ), hash (expected_data ))
3398
+ await self .server .wait_closed ()
3399
+ self .assertEqual (hash (self .server_buffer ), hash (expected_data ))
3477
3400
3478
3401
@requires_headers_trailers
3479
- def test_trailers (self ):
3402
+ async def test_trailers (self ):
3480
3403
TESTFN2 = os_helper .TESTFN + "2"
3481
3404
file_data = b"abcdef"
3482
3405
3483
3406
self .addCleanup (os_helper .unlink , TESTFN2 )
3484
3407
create_file (TESTFN2 , file_data )
3485
3408
3486
3409
with open (TESTFN2 , 'rb' ) as f :
3487
- os . sendfile (self .sockno , f .fileno (), 0 , 5 ,
3488
- trailers = [b"123456" , b"789" ])
3410
+ await self . async_sendfile (self .sockno , f .fileno (), 0 , 5 ,
3411
+ trailers = [b"123456" , b"789" ])
3489
3412
self .client .close ()
3490
- self .server .wait ()
3491
- data = self .server .handler_instance .get_data ()
3492
- self .assertEqual (data , b"abcde123456789" )
3413
+ await self .server .wait_closed ()
3414
+ self .assertEqual (self .server_buffer , b"abcde123456789" )
3493
3415
3494
3416
@requires_headers_trailers
3495
3417
@requires_32b
3496
- def test_headers_overflow_32bits (self ):
3418
+ async def test_headers_overflow_32bits (self ):
3497
3419
self .server .handler_instance .accumulate = False
3498
3420
with self .assertRaises (OSError ) as cm :
3499
- os . sendfile (self .sockno , self .fileno , 0 , 0 ,
3500
- headers = [b"x" * 2 ** 16 ] * 2 ** 15 )
3421
+ await self . async_sendfile (self .sockno , self .fileno , 0 , 0 ,
3422
+ headers = [b"x" * 2 ** 16 ] * 2 ** 15 )
3501
3423
self .assertEqual (cm .exception .errno , errno .EINVAL )
3502
3424
3503
3425
@requires_headers_trailers
3504
3426
@requires_32b
3505
- def test_trailers_overflow_32bits (self ):
3427
+ async def test_trailers_overflow_32bits (self ):
3506
3428
self .server .handler_instance .accumulate = False
3507
3429
with self .assertRaises (OSError ) as cm :
3508
- os . sendfile (self .sockno , self .fileno , 0 , 0 ,
3509
- trailers = [b"x" * 2 ** 16 ] * 2 ** 15 )
3430
+ await self . async_sendfile (self .sockno , self .fileno , 0 , 0 ,
3431
+ trailers = [b"x" * 2 ** 16 ] * 2 ** 15 )
3510
3432
self .assertEqual (cm .exception .errno , errno .EINVAL )
3511
3433
3512
3434
@requires_headers_trailers
3513
3435
@unittest .skipUnless (hasattr (os , 'SF_NODISKIO' ),
3514
3436
'test needs os.SF_NODISKIO' )
3515
- def test_flags (self ):
3437
+ async def test_flags (self ):
3516
3438
try :
3517
- os . sendfile (self .sockno , self .fileno , 0 , 4096 ,
3518
- flags = os .SF_NODISKIO )
3439
+ await self . async_sendfile (self .sockno , self .fileno , 0 , 4096 ,
3440
+ flags = os .SF_NODISKIO )
3519
3441
except OSError as err :
3520
3442
if err .errno not in (errno .EBUSY , errno .EAGAIN ):
3521
3443
raise
0 commit comments