Skip to content

Commit 3ae975f

Browse files
authored
bpo-47015: Update test_os from asyncore to asyncio (GH-31876)
1 parent e730ae7 commit 3ae975f

File tree

2 files changed

+81
-157
lines changed

2 files changed

+81
-157
lines changed

Lib/test/test_os.py

Lines changed: 79 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# does add tests for a few functions which have been determined to be more
33
# portable than they had been thought to be.
44

5+
import asyncio
56
import codecs
67
import contextlib
78
import decimal
@@ -23,7 +24,6 @@
2324
import sys
2425
import sysconfig
2526
import tempfile
26-
import threading
2727
import time
2828
import types
2929
import unittest
@@ -33,15 +33,9 @@
3333
from test.support import import_helper
3434
from test.support import os_helper
3535
from test.support import socket_helper
36-
from test.support import threading_helper
3736
from test.support import warnings_helper
3837
from platform import win32_is_iot
3938

40-
with warnings.catch_warnings():
41-
warnings.simplefilter('ignore', DeprecationWarning)
42-
import asynchat
43-
import asyncore
44-
4539
try:
4640
import resource
4741
except ImportError:
@@ -101,6 +95,10 @@ def create_file(filename, content=b'content'):
10195
'on AIX, splice() only accepts sockets')
10296

10397

98+
def tearDownModule():
99+
asyncio.set_event_loop_policy(None)
100+
101+
104102
class MiscTests(unittest.TestCase):
105103
def test_getcwd(self):
106104
cwd = os.getcwd()
@@ -3228,94 +3226,8 @@ def test_set_get_priority(self):
32283226
raise
32293227

32303228

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-
33173229
@unittest.skipUnless(hasattr(os, 'sendfile'), "test needs os.sendfile()")
3318-
class TestSendfile(unittest.TestCase):
3230+
class TestSendfile(unittest.IsolatedAsyncioTestCase):
33193231

33203232
DATA = b"12345abcde" * 16 * 1024 # 160 KiB
33213233
SUPPORT_HEADERS_TRAILERS = not sys.platform.startswith("linux") and \
@@ -3328,40 +3240,52 @@ class TestSendfile(unittest.TestCase):
33283240

33293241
@classmethod
33303242
def setUpClass(cls):
3331-
cls.key = threading_helper.threading_setup()
33323243
create_file(os_helper.TESTFN, cls.DATA)
33333244

33343245
@classmethod
33353246
def tearDownClass(cls):
3336-
threading_helper.threading_cleanup(*cls.key)
33373247
os_helper.unlink(os_helper.TESTFN)
33383248

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()
33423264
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)
33473267
self.sockno = self.client.fileno()
33483268
self.file = open(os_helper.TESTFN, 'rb')
33493269
self.fileno = self.file.fileno()
33503270

3351-
def tearDown(self):
3271+
async def asyncTearDown(self):
33523272
self.file.close()
33533273
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)
33573280

3358-
def sendfile_wrapper(self, *args, **kwargs):
3281+
@staticmethod
3282+
async def sendfile_wrapper(*args, **kwargs):
33593283
"""A higher level wrapper representing how an application is
33603284
supposed to use sendfile().
33613285
"""
33623286
while True:
33633287
try:
3364-
return os.sendfile(*args, **kwargs)
3288+
return await TestSendfile.async_sendfile(*args, **kwargs)
33653289
except OSError as err:
33663290
if err.errno == errno.ECONNRESET:
33673291
# disconnected
@@ -3372,13 +3296,14 @@ def sendfile_wrapper(self, *args, **kwargs):
33723296
else:
33733297
raise
33743298

3375-
def test_send_whole_file(self):
3299+
async def test_send_whole_file(self):
33763300
# normal send
33773301
total_sent = 0
33783302
offset = 0
33793303
nbytes = 4096
33803304
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)
33823307
if sent == 0:
33833308
break
33843309
offset += sent
@@ -3389,19 +3314,19 @@ def test_send_whole_file(self):
33893314
self.assertEqual(total_sent, len(self.DATA))
33903315
self.client.shutdown(socket.SHUT_RDWR)
33913316
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)
33963320

3397-
def test_send_at_certain_offset(self):
3321+
async def test_send_at_certain_offset(self):
33983322
# start sending a file at a certain offset
33993323
total_sent = 0
34003324
offset = len(self.DATA) // 2
34013325
must_send = len(self.DATA) - offset
34023326
nbytes = 4096
34033327
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)
34053330
if sent == 0:
34063331
break
34073332
offset += sent
@@ -3410,18 +3335,18 @@ def test_send_at_certain_offset(self):
34103335

34113336
self.client.shutdown(socket.SHUT_RDWR)
34123337
self.client.close()
3413-
self.server.wait()
3414-
data = self.server.handler_instance.get_data()
3338+
await self.server.wait_closed()
34153339
expected = self.DATA[len(self.DATA) // 2:]
34163340
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)
34193343

3420-
def test_offset_overflow(self):
3344+
async def test_offset_overflow(self):
34213345
# specify an offset > file size
34223346
offset = len(self.DATA) + 4096
34233347
try:
3424-
sent = os.sendfile(self.sockno, self.fileno, offset, 4096)
3348+
sent = await self.async_sendfile(self.sockno, self.fileno,
3349+
offset, 4096)
34253350
except OSError as e:
34263351
# Solaris can raise EINVAL if offset >= file length, ignore.
34273352
if e.errno != errno.EINVAL:
@@ -3430,39 +3355,38 @@ def test_offset_overflow(self):
34303355
self.assertEqual(sent, 0)
34313356
self.client.shutdown(socket.SHUT_RDWR)
34323357
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'')
34363360

3437-
def test_invalid_offset(self):
3361+
async def test_invalid_offset(self):
34383362
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)
34403364
self.assertEqual(cm.exception.errno, errno.EINVAL)
34413365

3442-
def test_keywords(self):
3366+
async def test_keywords(self):
34433367
# 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)
34463370
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)
34503374

34513375
# --- headers / trailers tests
34523376

34533377
@requires_headers_trailers
3454-
def test_headers(self):
3378+
async def test_headers(self):
34553379
total_sent = 0
34563380
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])
34593383
self.assertLessEqual(sent, 512 + 256 + 4096)
34603384
total_sent += sent
34613385
offset = 4096
34623386
while total_sent < len(expected_data):
34633387
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)
34663390
if sent == 0:
34673391
break
34683392
self.assertLessEqual(sent, nbytes)
@@ -3471,51 +3395,49 @@ def test_headers(self):
34713395

34723396
self.assertEqual(total_sent, len(expected_data))
34733397
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))
34773400

34783401
@requires_headers_trailers
3479-
def test_trailers(self):
3402+
async def test_trailers(self):
34803403
TESTFN2 = os_helper.TESTFN + "2"
34813404
file_data = b"abcdef"
34823405

34833406
self.addCleanup(os_helper.unlink, TESTFN2)
34843407
create_file(TESTFN2, file_data)
34853408

34863409
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"])
34893412
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")
34933415

34943416
@requires_headers_trailers
34953417
@requires_32b
3496-
def test_headers_overflow_32bits(self):
3418+
async def test_headers_overflow_32bits(self):
34973419
self.server.handler_instance.accumulate = False
34983420
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)
35013423
self.assertEqual(cm.exception.errno, errno.EINVAL)
35023424

35033425
@requires_headers_trailers
35043426
@requires_32b
3505-
def test_trailers_overflow_32bits(self):
3427+
async def test_trailers_overflow_32bits(self):
35063428
self.server.handler_instance.accumulate = False
35073429
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)
35103432
self.assertEqual(cm.exception.errno, errno.EINVAL)
35113433

35123434
@requires_headers_trailers
35133435
@unittest.skipUnless(hasattr(os, 'SF_NODISKIO'),
35143436
'test needs os.SF_NODISKIO')
3515-
def test_flags(self):
3437+
async def test_flags(self):
35163438
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)
35193441
except OSError as err:
35203442
if err.errno not in (errno.EBUSY, errno.EAGAIN):
35213443
raise

0 commit comments

Comments
 (0)