mirror of https://github.com/python/cpython
bpo-47015: Update test_os from asyncore to asyncio (GH-31876)
This commit is contained in:
parent
e730ae7eff
commit
3ae975f1ac
|
@ -2,6 +2,7 @@
|
|||
# does add tests for a few functions which have been determined to be more
|
||||
# portable than they had been thought to be.
|
||||
|
||||
import asyncio
|
||||
import codecs
|
||||
import contextlib
|
||||
import decimal
|
||||
|
@ -23,7 +24,6 @@ import subprocess
|
|||
import sys
|
||||
import sysconfig
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import types
|
||||
import unittest
|
||||
|
@ -33,15 +33,9 @@ from test import support
|
|||
from test.support import import_helper
|
||||
from test.support import os_helper
|
||||
from test.support import socket_helper
|
||||
from test.support import threading_helper
|
||||
from test.support import warnings_helper
|
||||
from platform import win32_is_iot
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore', DeprecationWarning)
|
||||
import asynchat
|
||||
import asyncore
|
||||
|
||||
try:
|
||||
import resource
|
||||
except ImportError:
|
||||
|
@ -101,6 +95,10 @@ requires_splice_pipe = unittest.skipIf(sys.platform.startswith("aix"),
|
|||
'on AIX, splice() only accepts sockets')
|
||||
|
||||
|
||||
def tearDownModule():
|
||||
asyncio.set_event_loop_policy(None)
|
||||
|
||||
|
||||
class MiscTests(unittest.TestCase):
|
||||
def test_getcwd(self):
|
||||
cwd = os.getcwd()
|
||||
|
@ -3228,94 +3226,8 @@ class ProgramPriorityTests(unittest.TestCase):
|
|||
raise
|
||||
|
||||
|
||||
class SendfileTestServer(asyncore.dispatcher, threading.Thread):
|
||||
|
||||
class Handler(asynchat.async_chat):
|
||||
|
||||
def __init__(self, conn):
|
||||
asynchat.async_chat.__init__(self, conn)
|
||||
self.in_buffer = []
|
||||
self.accumulate = True
|
||||
self.closed = False
|
||||
self.push(b"220 ready\r\n")
|
||||
|
||||
def handle_read(self):
|
||||
data = self.recv(4096)
|
||||
if self.accumulate:
|
||||
self.in_buffer.append(data)
|
||||
|
||||
def get_data(self):
|
||||
return b''.join(self.in_buffer)
|
||||
|
||||
def handle_close(self):
|
||||
self.close()
|
||||
self.closed = True
|
||||
|
||||
def handle_error(self):
|
||||
raise
|
||||
|
||||
def __init__(self, address):
|
||||
threading.Thread.__init__(self)
|
||||
asyncore.dispatcher.__init__(self)
|
||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.bind(address)
|
||||
self.listen(5)
|
||||
self.host, self.port = self.socket.getsockname()[:2]
|
||||
self.handler_instance = None
|
||||
self._active = False
|
||||
self._active_lock = threading.Lock()
|
||||
|
||||
# --- public API
|
||||
|
||||
@property
|
||||
def running(self):
|
||||
return self._active
|
||||
|
||||
def start(self):
|
||||
assert not self.running
|
||||
self.__flag = threading.Event()
|
||||
threading.Thread.start(self)
|
||||
self.__flag.wait()
|
||||
|
||||
def stop(self):
|
||||
assert self.running
|
||||
self._active = False
|
||||
self.join()
|
||||
|
||||
def wait(self):
|
||||
# wait for handler connection to be closed, then stop the server
|
||||
while not getattr(self.handler_instance, "closed", False):
|
||||
time.sleep(0.001)
|
||||
self.stop()
|
||||
|
||||
# --- internals
|
||||
|
||||
def run(self):
|
||||
self._active = True
|
||||
self.__flag.set()
|
||||
while self._active and asyncore.socket_map:
|
||||
self._active_lock.acquire()
|
||||
asyncore.loop(timeout=0.001, count=1)
|
||||
self._active_lock.release()
|
||||
asyncore.close_all()
|
||||
|
||||
def handle_accept(self):
|
||||
conn, addr = self.accept()
|
||||
self.handler_instance = self.Handler(conn)
|
||||
|
||||
def handle_connect(self):
|
||||
self.close()
|
||||
handle_read = handle_connect
|
||||
|
||||
def writable(self):
|
||||
return 0
|
||||
|
||||
def handle_error(self):
|
||||
raise
|
||||
|
||||
|
||||
@unittest.skipUnless(hasattr(os, 'sendfile'), "test needs os.sendfile()")
|
||||
class TestSendfile(unittest.TestCase):
|
||||
class TestSendfile(unittest.IsolatedAsyncioTestCase):
|
||||
|
||||
DATA = b"12345abcde" * 16 * 1024 # 160 KiB
|
||||
SUPPORT_HEADERS_TRAILERS = not sys.platform.startswith("linux") and \
|
||||
|
@ -3328,40 +3240,52 @@ class TestSendfile(unittest.TestCase):
|
|||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.key = threading_helper.threading_setup()
|
||||
create_file(os_helper.TESTFN, cls.DATA)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
threading_helper.threading_cleanup(*cls.key)
|
||||
os_helper.unlink(os_helper.TESTFN)
|
||||
|
||||
def setUp(self):
|
||||
self.server = SendfileTestServer((socket_helper.HOST, 0))
|
||||
self.server.start()
|
||||
@staticmethod
|
||||
async def chunks(reader):
|
||||
while not reader.at_eof():
|
||||
yield await reader.read()
|
||||
|
||||
async def handle_new_client(self, reader, writer):
|
||||
self.server_buffer = b''.join([x async for x in self.chunks(reader)])
|
||||
writer.close()
|
||||
self.server.close() # The test server processes a single client only
|
||||
|
||||
async def asyncSetUp(self):
|
||||
self.server_buffer = b''
|
||||
self.server = await asyncio.start_server(self.handle_new_client,
|
||||
socket_helper.HOSTv4)
|
||||
server_name = self.server.sockets[0].getsockname()
|
||||
self.client = socket.socket()
|
||||
self.client.connect((self.server.host, self.server.port))
|
||||
self.client.settimeout(1)
|
||||
# synchronize by waiting for "220 ready" response
|
||||
self.client.recv(1024)
|
||||
self.client.setblocking(False)
|
||||
await asyncio.get_running_loop().sock_connect(self.client, server_name)
|
||||
self.sockno = self.client.fileno()
|
||||
self.file = open(os_helper.TESTFN, 'rb')
|
||||
self.fileno = self.file.fileno()
|
||||
|
||||
def tearDown(self):
|
||||
async def asyncTearDown(self):
|
||||
self.file.close()
|
||||
self.client.close()
|
||||
if self.server.running:
|
||||
self.server.stop()
|
||||
self.server = None
|
||||
await self.server.wait_closed()
|
||||
|
||||
def sendfile_wrapper(self, *args, **kwargs):
|
||||
# Use the test subject instead of asyncio.loop.sendfile
|
||||
@staticmethod
|
||||
async def async_sendfile(*args, **kwargs):
|
||||
return await asyncio.to_thread(os.sendfile, *args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
async def sendfile_wrapper(*args, **kwargs):
|
||||
"""A higher level wrapper representing how an application is
|
||||
supposed to use sendfile().
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
return os.sendfile(*args, **kwargs)
|
||||
return await TestSendfile.async_sendfile(*args, **kwargs)
|
||||
except OSError as err:
|
||||
if err.errno == errno.ECONNRESET:
|
||||
# disconnected
|
||||
|
@ -3372,13 +3296,14 @@ class TestSendfile(unittest.TestCase):
|
|||
else:
|
||||
raise
|
||||
|
||||
def test_send_whole_file(self):
|
||||
async def test_send_whole_file(self):
|
||||
# normal send
|
||||
total_sent = 0
|
||||
offset = 0
|
||||
nbytes = 4096
|
||||
while total_sent < len(self.DATA):
|
||||
sent = self.sendfile_wrapper(self.sockno, self.fileno, offset, nbytes)
|
||||
sent = await self.sendfile_wrapper(self.sockno, self.fileno,
|
||||
offset, nbytes)
|
||||
if sent == 0:
|
||||
break
|
||||
offset += sent
|
||||
|
@ -3389,19 +3314,19 @@ class TestSendfile(unittest.TestCase):
|
|||
self.assertEqual(total_sent, len(self.DATA))
|
||||
self.client.shutdown(socket.SHUT_RDWR)
|
||||
self.client.close()
|
||||
self.server.wait()
|
||||
data = self.server.handler_instance.get_data()
|
||||
self.assertEqual(len(data), len(self.DATA))
|
||||
self.assertEqual(data, self.DATA)
|
||||
await self.server.wait_closed()
|
||||
self.assertEqual(len(self.server_buffer), len(self.DATA))
|
||||
self.assertEqual(self.server_buffer, self.DATA)
|
||||
|
||||
def test_send_at_certain_offset(self):
|
||||
async def test_send_at_certain_offset(self):
|
||||
# start sending a file at a certain offset
|
||||
total_sent = 0
|
||||
offset = len(self.DATA) // 2
|
||||
must_send = len(self.DATA) - offset
|
||||
nbytes = 4096
|
||||
while total_sent < must_send:
|
||||
sent = self.sendfile_wrapper(self.sockno, self.fileno, offset, nbytes)
|
||||
sent = await self.sendfile_wrapper(self.sockno, self.fileno,
|
||||
offset, nbytes)
|
||||
if sent == 0:
|
||||
break
|
||||
offset += sent
|
||||
|
@ -3410,18 +3335,18 @@ class TestSendfile(unittest.TestCase):
|
|||
|
||||
self.client.shutdown(socket.SHUT_RDWR)
|
||||
self.client.close()
|
||||
self.server.wait()
|
||||
data = self.server.handler_instance.get_data()
|
||||
await self.server.wait_closed()
|
||||
expected = self.DATA[len(self.DATA) // 2:]
|
||||
self.assertEqual(total_sent, len(expected))
|
||||
self.assertEqual(len(data), len(expected))
|
||||
self.assertEqual(data, expected)
|
||||
self.assertEqual(len(self.server_buffer), len(expected))
|
||||
self.assertEqual(self.server_buffer, expected)
|
||||
|
||||
def test_offset_overflow(self):
|
||||
async def test_offset_overflow(self):
|
||||
# specify an offset > file size
|
||||
offset = len(self.DATA) + 4096
|
||||
try:
|
||||
sent = os.sendfile(self.sockno, self.fileno, offset, 4096)
|
||||
sent = await self.async_sendfile(self.sockno, self.fileno,
|
||||
offset, 4096)
|
||||
except OSError as e:
|
||||
# Solaris can raise EINVAL if offset >= file length, ignore.
|
||||
if e.errno != errno.EINVAL:
|
||||
|
@ -3430,39 +3355,38 @@ class TestSendfile(unittest.TestCase):
|
|||
self.assertEqual(sent, 0)
|
||||
self.client.shutdown(socket.SHUT_RDWR)
|
||||
self.client.close()
|
||||
self.server.wait()
|
||||
data = self.server.handler_instance.get_data()
|
||||
self.assertEqual(data, b'')
|
||||
await self.server.wait_closed()
|
||||
self.assertEqual(self.server_buffer, b'')
|
||||
|
||||
def test_invalid_offset(self):
|
||||
async def test_invalid_offset(self):
|
||||
with self.assertRaises(OSError) as cm:
|
||||
os.sendfile(self.sockno, self.fileno, -1, 4096)
|
||||
await self.async_sendfile(self.sockno, self.fileno, -1, 4096)
|
||||
self.assertEqual(cm.exception.errno, errno.EINVAL)
|
||||
|
||||
def test_keywords(self):
|
||||
async def test_keywords(self):
|
||||
# Keyword arguments should be supported
|
||||
os.sendfile(out_fd=self.sockno, in_fd=self.fileno,
|
||||
offset=0, count=4096)
|
||||
await self.async_sendfile(out_fd=self.sockno, in_fd=self.fileno,
|
||||
offset=0, count=4096)
|
||||
if self.SUPPORT_HEADERS_TRAILERS:
|
||||
os.sendfile(out_fd=self.sockno, in_fd=self.fileno,
|
||||
offset=0, count=4096,
|
||||
headers=(), trailers=(), flags=0)
|
||||
await self.async_sendfile(out_fd=self.sockno, in_fd=self.fileno,
|
||||
offset=0, count=4096,
|
||||
headers=(), trailers=(), flags=0)
|
||||
|
||||
# --- headers / trailers tests
|
||||
|
||||
@requires_headers_trailers
|
||||
def test_headers(self):
|
||||
async def test_headers(self):
|
||||
total_sent = 0
|
||||
expected_data = b"x" * 512 + b"y" * 256 + self.DATA[:-1]
|
||||
sent = os.sendfile(self.sockno, self.fileno, 0, 4096,
|
||||
headers=[b"x" * 512, b"y" * 256])
|
||||
sent = await self.async_sendfile(self.sockno, self.fileno, 0, 4096,
|
||||
headers=[b"x" * 512, b"y" * 256])
|
||||
self.assertLessEqual(sent, 512 + 256 + 4096)
|
||||
total_sent += sent
|
||||
offset = 4096
|
||||
while total_sent < len(expected_data):
|
||||
nbytes = min(len(expected_data) - total_sent, 4096)
|
||||
sent = self.sendfile_wrapper(self.sockno, self.fileno,
|
||||
offset, nbytes)
|
||||
sent = await self.sendfile_wrapper(self.sockno, self.fileno,
|
||||
offset, nbytes)
|
||||
if sent == 0:
|
||||
break
|
||||
self.assertLessEqual(sent, nbytes)
|
||||
|
@ -3471,12 +3395,11 @@ class TestSendfile(unittest.TestCase):
|
|||
|
||||
self.assertEqual(total_sent, len(expected_data))
|
||||
self.client.close()
|
||||
self.server.wait()
|
||||
data = self.server.handler_instance.get_data()
|
||||
self.assertEqual(hash(data), hash(expected_data))
|
||||
await self.server.wait_closed()
|
||||
self.assertEqual(hash(self.server_buffer), hash(expected_data))
|
||||
|
||||
@requires_headers_trailers
|
||||
def test_trailers(self):
|
||||
async def test_trailers(self):
|
||||
TESTFN2 = os_helper.TESTFN + "2"
|
||||
file_data = b"abcdef"
|
||||
|
||||
|
@ -3484,38 +3407,37 @@ class TestSendfile(unittest.TestCase):
|
|||
create_file(TESTFN2, file_data)
|
||||
|
||||
with open(TESTFN2, 'rb') as f:
|
||||
os.sendfile(self.sockno, f.fileno(), 0, 5,
|
||||
trailers=[b"123456", b"789"])
|
||||
await self.async_sendfile(self.sockno, f.fileno(), 0, 5,
|
||||
trailers=[b"123456", b"789"])
|
||||
self.client.close()
|
||||
self.server.wait()
|
||||
data = self.server.handler_instance.get_data()
|
||||
self.assertEqual(data, b"abcde123456789")
|
||||
await self.server.wait_closed()
|
||||
self.assertEqual(self.server_buffer, b"abcde123456789")
|
||||
|
||||
@requires_headers_trailers
|
||||
@requires_32b
|
||||
def test_headers_overflow_32bits(self):
|
||||
async def test_headers_overflow_32bits(self):
|
||||
self.server.handler_instance.accumulate = False
|
||||
with self.assertRaises(OSError) as cm:
|
||||
os.sendfile(self.sockno, self.fileno, 0, 0,
|
||||
headers=[b"x" * 2**16] * 2**15)
|
||||
await self.async_sendfile(self.sockno, self.fileno, 0, 0,
|
||||
headers=[b"x" * 2**16] * 2**15)
|
||||
self.assertEqual(cm.exception.errno, errno.EINVAL)
|
||||
|
||||
@requires_headers_trailers
|
||||
@requires_32b
|
||||
def test_trailers_overflow_32bits(self):
|
||||
async def test_trailers_overflow_32bits(self):
|
||||
self.server.handler_instance.accumulate = False
|
||||
with self.assertRaises(OSError) as cm:
|
||||
os.sendfile(self.sockno, self.fileno, 0, 0,
|
||||
trailers=[b"x" * 2**16] * 2**15)
|
||||
await self.async_sendfile(self.sockno, self.fileno, 0, 0,
|
||||
trailers=[b"x" * 2**16] * 2**15)
|
||||
self.assertEqual(cm.exception.errno, errno.EINVAL)
|
||||
|
||||
@requires_headers_trailers
|
||||
@unittest.skipUnless(hasattr(os, 'SF_NODISKIO'),
|
||||
'test needs os.SF_NODISKIO')
|
||||
def test_flags(self):
|
||||
async def test_flags(self):
|
||||
try:
|
||||
os.sendfile(self.sockno, self.fileno, 0, 4096,
|
||||
flags=os.SF_NODISKIO)
|
||||
await self.async_sendfile(self.sockno, self.fileno, 0, 4096,
|
||||
flags=os.SF_NODISKIO)
|
||||
except OSError as err:
|
||||
if err.errno not in (errno.EBUSY, errno.EAGAIN):
|
||||
raise
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
A test case for :func:`os.sendfile` is converted from deprecated
|
||||
:mod:`asyncore` (see :pep:`594`) to :mod:`asyncio`. Patch by Oleg Iarygin.
|
Loading…
Reference in New Issue