diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index e63180b750a24a..6cee171b65aaea 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -726,7 +726,7 @@ Low-level socket operations the file when the platform does not support the sendfile syscall (e.g. Windows or SSL socket on Unix). - Raise :exc:`RuntimeError` if the system does not support + Raise :exc:`SendfileNotAvailableError` if the system does not support *sendfile* syscall and *fallback* is ``False``. .. versionadded:: 3.7 @@ -980,6 +980,18 @@ Handle .. versionadded:: 3.7 +SendfileNotAvailableError +------------------------- + + +.. exception:: SendfileNotAvailableError + + Sendfile syscall is not available, subclass of :exc:`RuntimeError`. + + Raised if the OS does not support senfile syscall for + given socket or file type. + + Event loop examples ------------------- diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index b6a9384d95b4d2..00c84a835c8bd1 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -154,10 +154,6 @@ def _run_until_complete_cb(fut): futures._get_loop(fut).stop() -class _SendfileNotAvailable(RuntimeError): - pass - - class Server(events.AbstractServer): def __init__(self, loop, sockets): @@ -659,17 +655,16 @@ async def sock_sendfile(self, sock, file, offset=0, count=None, try: return await self._sock_sendfile_native(sock, file, offset, count) - except _SendfileNotAvailable as exc: - if fallback: - return await self._sock_sendfile_fallback(sock, file, - offset, count) - else: - raise RuntimeError(exc.args[0]) from None + except events.SendfileNotAvailableError as exc: + if not fallback: + raise + return await self._sock_sendfile_fallback(sock, file, + offset, count) async def _sock_sendfile_native(self, sock, file, offset, count): # NB: sendfile syscall is not supported for SSL sockets and # non-mmap files even if sendfile is supported by OS - raise _SendfileNotAvailable( + raise events.SendfileNotAvailableError( f"syscall sendfile is not available for socket {sock!r} " "and file {file!r} combination") diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index b06721f3b038c8..d5365dc480d3d3 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -3,7 +3,7 @@ __all__ = ( 'AbstractEventLoopPolicy', 'AbstractEventLoop', 'AbstractServer', - 'Handle', 'TimerHandle', + 'Handle', 'TimerHandle', 'SendfileNotAvailableError', 'get_event_loop_policy', 'set_event_loop_policy', 'get_event_loop', 'set_event_loop', 'new_event_loop', 'get_child_watcher', 'set_child_watcher', @@ -20,6 +20,14 @@ from . import format_helpers +class SendfileNotAvailableError(RuntimeError): + """Sendfile syscall is not available. + + Raised if OS does not support senfile syscall for given socket or + file type. + """ + + class Handle: """Object returned by callback registration methods.""" diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index f40ef12f2655fa..028a0ca8f8388a 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -313,16 +313,16 @@ async def _sock_sendfile_native(self, sock, file, offset, count): try: os.sendfile except AttributeError as exc: - raise base_events._SendfileNotAvailable( + raise events.SendfileNotAvailableError( "os.sendfile() is not available") try: fileno = file.fileno() except (AttributeError, io.UnsupportedOperation) as err: - raise base_events._SendfileNotAvailable("not a regular file") + raise events.SendfileNotAvailableError("not a regular file") try: fsize = os.fstat(fileno).st_size except OSError as err: - raise base_events._SendfileNotAvailable("not a regular file") + raise events.SendfileNotAvailableError("not a regular file") blocksize = count if count else fsize if not blocksize: return 0 # empty file @@ -365,7 +365,7 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno, # one being 'file' is not a regular mmap(2)-like # file, in which case we'll fall back on using # plain send(). - err = base_events._SendfileNotAvailable( + err = events.SendfileNotAvailableError( "os.sendfile call failed") self._sock_sendfile_update_filepos(fileno, offset, total_sent) fut.set_exception(err) diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 085124f53724c8..fc3b81096dabe4 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -14,6 +14,7 @@ import asyncio from asyncio import base_events from asyncio import constants +from asyncio import events from test.test_asyncio import utils as test_utils from test import support from test.support.script_helper import assert_python_ok @@ -1860,7 +1861,7 @@ def cleanup(): def test__sock_sendfile_native_failure(self): sock, proto = self.prepare() - with self.assertRaisesRegex(base_events._SendfileNotAvailable, + with self.assertRaisesRegex(events.SendfileNotAvailableError, "sendfile is not available"): self.run_loop(self.loop._sock_sendfile_native(sock, self.file, 0, None)) @@ -1871,7 +1872,7 @@ def test__sock_sendfile_native_failure(self): def test_sock_sendfile_no_fallback(self): sock, proto = self.prepare() - with self.assertRaisesRegex(RuntimeError, + with self.assertRaisesRegex(events.SendfileNotAvailableError, "sendfile is not available"): self.run_loop(self.loop.sock_sendfile(sock, self.file, fallback=False)) diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 4e2b76bb47280f..8646bf765e934f 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -23,6 +23,7 @@ import asyncio from asyncio import log from asyncio import base_events +from asyncio import events from asyncio import unix_events from test.test_asyncio import utils as test_utils @@ -517,7 +518,7 @@ def test_with_offset_and_count(self): def test_sendfile_not_available(self): sock, proto = self.prepare() with mock.patch('asyncio.unix_events.os', spec=[]): - with self.assertRaisesRegex(base_events._SendfileNotAvailable, + with self.assertRaisesRegex(events.SendfileNotAvailableError, "os[.]sendfile[(][)] is not available"): self.run_loop(self.loop._sock_sendfile_native(sock, self.file, 0, None)) @@ -526,7 +527,7 @@ def test_sendfile_not_available(self): def test_sendfile_not_a_file(self): sock, proto = self.prepare() f = object() - with self.assertRaisesRegex(base_events._SendfileNotAvailable, + with self.assertRaisesRegex(events.SendfileNotAvailableError, "not a regular file"): self.run_loop(self.loop._sock_sendfile_native(sock, f, 0, None)) @@ -535,7 +536,7 @@ def test_sendfile_not_a_file(self): def test_sendfile_iobuffer(self): sock, proto = self.prepare() f = io.BytesIO() - with self.assertRaisesRegex(base_events._SendfileNotAvailable, + with self.assertRaisesRegex(events.SendfileNotAvailableError, "not a regular file"): self.run_loop(self.loop._sock_sendfile_native(sock, f, 0, None)) @@ -545,7 +546,7 @@ def test_sendfile_not_regular_file(self): sock, proto = self.prepare() f = mock.Mock() f.fileno.return_value = -1 - with self.assertRaisesRegex(base_events._SendfileNotAvailable, + with self.assertRaisesRegex(events.SendfileNotAvailableError, "not a regular file"): self.run_loop(self.loop._sock_sendfile_native(sock, f, 0, None)) @@ -631,7 +632,7 @@ def test_os_error_first_call(self): with self.assertRaises(KeyError): self.loop._selector.get_key(sock) exc = fut.exception() - self.assertIsInstance(exc, base_events._SendfileNotAvailable) + self.assertIsInstance(exc, events.SendfileNotAvailableError) self.assertEqual(0, self.file.tell()) def test_os_error_next_call(self): @@ -656,7 +657,7 @@ def test_exception(self): fileno = self.file.fileno() fut = self.loop.create_future() - err = RuntimeError() + err = events.SendfileNotAvailableError() with mock.patch('os.sendfile', side_effect=err): self.loop._sock_sendfile_native_impl(fut, sock.fileno(), sock, fileno,