From 0df6a92367a709404cbbb6ebb5dd0291686f4e60 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 13 Jul 2026 15:50:26 +0530 Subject: [PATCH 1/4] gh-71019: Document which objects connect_read_pipe/connect_write_pipe accept The docs described *pipe* as simply a file-like object, which suggested that regular files and the standard streams would work. They do not: on Unix only pipes, sockets and character devices are accepted (anything else raises ValueError), and on Windows only handles opened for overlapped I/O can be associated with an I/O completion port, so console handles and file handles fail asynchronously rather than at call time. Spell out what is accepted on each platform and note that regular files are never supported. --- Doc/library/asyncio-eventloop.rst | 46 +++++++++++++++++++++++++++++-- Doc/library/asyncio-platforms.rst | 18 ++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index e6cdd4e57475b2..d2b0c8c1ab0414 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -1263,7 +1263,9 @@ Working with pipes *protocol_factory* must be a callable returning an :ref:`asyncio protocol ` implementation. - *pipe* is a :term:`file-like object `. + *pipe* is a :term:`file-like object `. Not every file-like + object is accepted; see :ref:`Supported pipe objects + ` below. Return pair ``(transport, protocol)``, where *transport* supports the :class:`ReadTransport` interface and *protocol* is an object @@ -1280,7 +1282,9 @@ Working with pipes *protocol_factory* must be a callable returning an :ref:`asyncio protocol ` implementation. - *pipe* is :term:`file-like object `. + *pipe* is a :term:`file-like object `. Not every file-like + object is accepted; see :ref:`Supported pipe objects + ` below. Return pair ``(transport, protocol)``, where *transport* supports :class:`WriteTransport` interface and *protocol* is an object @@ -1289,6 +1293,44 @@ Working with pipes With :class:`SelectorEventLoop` event loop, the *pipe* is set to non-blocking mode. +.. _asyncio-pipe-objects: + +.. rubric:: Supported pipe objects + +Even though the *pipe* argument is a :term:`file-like object `, +these methods only work with objects the operating system can poll for +readiness or perform overlapped I/O on. Regular files on disk are **not** +supported on any platform, and neither are :data:`sys.stdin`, +:data:`sys.stdout` and :data:`sys.stderr` when they have been redirected to +or from a regular file. There is no asynchronous file I/O in asyncio; use +:meth:`loop.run_in_executor` to read and write regular files without +blocking the event loop. + +On Unix, with :class:`SelectorEventLoop`, *pipe* must wrap one of the +following: + +* a pipe, such as an end of an :func:`os.pipe` pair or a FIFO created with + :func:`os.mkfifo`; +* a socket; +* a character device, such as a terminal. This is why :data:`sys.stdin` + works when the program is run interactively but fails when its input is + redirected from a file. + +Anything else, a regular file in particular, raises :exc:`ValueError`. + +On Windows, where only :class:`ProactorEventLoop` implements these methods, +*pipe* must wrap a handle opened for overlapped I/O, since the handle has to +be associated with an I/O completion port. In practice this means a named +pipe, such as the ones created by :func:`!asyncio.windows_utils.pipe` and +used for the standard streams of a subprocess started by +:meth:`loop.subprocess_exec`. Console handles and regular file handles +cannot be associated with a completion port, so :data:`sys.stdin` and files +opened with :func:`open` do not work. Unlike on Unix, this is not reported +by the method itself: it returns successfully and the resulting +:exc:`OSError` is later passed to the +:meth:`event loop exception handler ` when the +transport first reads or writes. + .. note:: :class:`SelectorEventLoop` does not support the above methods on diff --git a/Doc/library/asyncio-platforms.rst b/Doc/library/asyncio-platforms.rst index a2a3114ad6e4c5..ef0d46ee61caad 100644 --- a/Doc/library/asyncio-platforms.rst +++ b/Doc/library/asyncio-platforms.rst @@ -19,6 +19,13 @@ All Platforms * :meth:`loop.add_reader` and :meth:`loop.add_writer` cannot be used to monitor file I/O. +* :meth:`loop.connect_read_pipe` and :meth:`loop.connect_write_pipe` + cannot be used with regular files, nor with :data:`sys.stdin`, + :data:`sys.stdout` or :data:`sys.stderr` when these are redirected to or + from a regular file. See :ref:`Supported pipe objects + ` for the objects that are accepted on each + platform. + Windows ======= @@ -62,6 +69,17 @@ All event loops on Windows do not support the following methods: * The :meth:`loop.add_reader` and :meth:`loop.add_writer` methods are not supported. +* :meth:`loop.connect_read_pipe` and :meth:`loop.connect_write_pipe` only + accept a handle opened for overlapped I/O, which in practice means a named + pipe such as those created by :func:`!asyncio.windows_utils.pipe`. + Console handles and regular file handles cannot be associated with an I/O + completion port, so :data:`sys.stdin`, :data:`sys.stdout`, + :data:`sys.stderr` and files opened with :func:`open` are not supported. + These methods do not fail immediately in that case: the resulting + :exc:`OSError` is passed to the + :meth:`event loop exception handler ` when the + transport first reads or writes. + The resolution of the monotonic clock on Windows is usually around 15.6 milliseconds. The best resolution is 0.5 milliseconds. The resolution depends on the hardware (availability of `HPET From 514b46e275ae6aeea1ed0969aaf3c324b7224c2b Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 13 Jul 2026 16:08:58 +0530 Subject: [PATCH 2/4] add tests --- Doc/library/asyncio-eventloop.rst | 38 +++----- Doc/library/asyncio-platforms.rst | 17 ++-- Lib/test/test_asyncio/test_events.py | 96 ++++++++++++++++++++ Lib/test/test_asyncio/test_windows_events.py | 86 ++++++++++++++++++ 4 files changed, 199 insertions(+), 38 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index d2b0c8c1ab0414..fb0fbb6e3b0415 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -1263,9 +1263,9 @@ Working with pipes *protocol_factory* must be a callable returning an :ref:`asyncio protocol ` implementation. - *pipe* is a :term:`file-like object `. Not every file-like - object is accepted; see :ref:`Supported pipe objects - ` below. + *pipe* is a :term:`file-like object `. See + :ref:`Supported pipe objects ` for the objects + supported as *pipe*. Return pair ``(transport, protocol)``, where *transport* supports the :class:`ReadTransport` interface and *protocol* is an object @@ -1282,9 +1282,9 @@ Working with pipes *protocol_factory* must be a callable returning an :ref:`asyncio protocol ` implementation. - *pipe* is a :term:`file-like object `. Not every file-like - object is accepted; see :ref:`Supported pipe objects - ` below. + *pipe* is a :term:`file-like object `. See + :ref:`Supported pipe objects ` for the objects + supported as *pipe*. Return pair ``(transport, protocol)``, where *transport* supports :class:`WriteTransport` interface and *protocol* is an object @@ -1297,13 +1297,10 @@ Working with pipes .. rubric:: Supported pipe objects -Even though the *pipe* argument is a :term:`file-like object `, -these methods only work with objects the operating system can poll for +These methods only work with objects the operating system can poll for readiness or perform overlapped I/O on. Regular files on disk are **not** -supported on any platform, and neither are :data:`sys.stdin`, -:data:`sys.stdout` and :data:`sys.stderr` when they have been redirected to -or from a regular file. There is no asynchronous file I/O in asyncio; use -:meth:`loop.run_in_executor` to read and write regular files without +supported on any platform. There is no asynchronous file I/O in asyncio; +use :meth:`loop.run_in_executor` to read and write regular files without blocking the event loop. On Unix, with :class:`SelectorEventLoop`, *pipe* must wrap one of the @@ -1312,24 +1309,11 @@ following: * a pipe, such as an end of an :func:`os.pipe` pair or a FIFO created with :func:`os.mkfifo`; * a socket; -* a character device, such as a terminal. This is why :data:`sys.stdin` - works when the program is run interactively but fails when its input is - redirected from a file. - -Anything else, a regular file in particular, raises :exc:`ValueError`. +* a character device, such as a terminal. On Windows, where only :class:`ProactorEventLoop` implements these methods, *pipe* must wrap a handle opened for overlapped I/O, since the handle has to -be associated with an I/O completion port. In practice this means a named -pipe, such as the ones created by :func:`!asyncio.windows_utils.pipe` and -used for the standard streams of a subprocess started by -:meth:`loop.subprocess_exec`. Console handles and regular file handles -cannot be associated with a completion port, so :data:`sys.stdin` and files -opened with :func:`open` do not work. Unlike on Unix, this is not reported -by the method itself: it returns successfully and the resulting -:exc:`OSError` is later passed to the -:meth:`event loop exception handler ` when the -transport first reads or writes. +be associated with an I/O completion port. .. note:: diff --git a/Doc/library/asyncio-platforms.rst b/Doc/library/asyncio-platforms.rst index ef0d46ee61caad..b8727f1bc4005e 100644 --- a/Doc/library/asyncio-platforms.rst +++ b/Doc/library/asyncio-platforms.rst @@ -20,9 +20,7 @@ All Platforms cannot be used to monitor file I/O. * :meth:`loop.connect_read_pipe` and :meth:`loop.connect_write_pipe` - cannot be used with regular files, nor with :data:`sys.stdin`, - :data:`sys.stdout` or :data:`sys.stderr` when these are redirected to or - from a regular file. See :ref:`Supported pipe objects + cannot be used with regular files. See :ref:`Supported pipe objects ` for the objects that are accepted on each platform. @@ -71,14 +69,11 @@ All event loops on Windows do not support the following methods: * :meth:`loop.connect_read_pipe` and :meth:`loop.connect_write_pipe` only accept a handle opened for overlapped I/O, which in practice means a named - pipe such as those created by :func:`!asyncio.windows_utils.pipe`. - Console handles and regular file handles cannot be associated with an I/O - completion port, so :data:`sys.stdin`, :data:`sys.stdout`, - :data:`sys.stderr` and files opened with :func:`open` are not supported. - These methods do not fail immediately in that case: the resulting - :exc:`OSError` is passed to the - :meth:`event loop exception handler ` when the - transport first reads or writes. + pipe such as those created by :func:`!asyncio.windows_utils.pipe`. A + console handle cannot be associated with an I/O completion port, so + :data:`sys.stdin`, :data:`sys.stdout` and :data:`sys.stderr` are not + supported. See :ref:`Supported pipe objects ` for + how such a handle is rejected, which differs between the two methods. The resolution of the monotonic clock on Windows is usually around 15.6 milliseconds. The best resolution is 0.5 milliseconds. The resolution depends on the diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index fed9bf67217762..91882f41d9428e 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -33,6 +33,7 @@ from multiprocessing.util import _cleanup_tests as multiprocessing_cleanup_tests from test.test_asyncio import utils as test_utils from test import support +from test.support import os_helper from test.support import socket_helper from test.support import threading_helper from test.support import ALWAYS_EQ, LARGEST, SMALLEST @@ -3134,5 +3135,100 @@ def test_get_loop(self): events.AbstractServer().get_loop() +@unittest.skipIf(sys.platform == 'win32', 'Unix pipe transport semantics') +class UnixPipeObjectSupportTests(unittest.TestCase): + def setUp(self): + super().setUp() + self.loop = asyncio.SelectorEventLoop() + self.addCleanup(self.loop.close) + + def check_accepted(self, connect, pipeobj): + lost = self.loop.create_future() + + class Proto(asyncio.Protocol): + def connection_lost(self, exc): + if not lost.done(): + lost.set_result(exc) + + async def run(): + transport, protocol = await connect(Proto, pipeobj) + self.assertIsInstance(protocol, Proto) + self.assertFalse(pipeobj.closed) + transport.close() + self.assertIsNone(await lost) + + self.loop.run_until_complete(run()) + self.assertTrue(pipeobj.closed) + + def check_rejected(self, connect, pipeobj): + self.addCleanup(pipeobj.close) + with self.assertRaisesRegex(ValueError, 'Pipe transport is'): + self.loop.run_until_complete(connect(asyncio.Protocol, pipeobj)) + + def test_read_pipe(self): + rfd, wfd = os.pipe() + self.addCleanup(os.close, wfd) + self.check_accepted(self.loop.connect_read_pipe, open(rfd, 'rb', 0)) + + def test_write_pipe(self): + rfd, wfd = os.pipe() + self.addCleanup(os.close, rfd) + self.check_accepted(self.loop.connect_write_pipe, open(wfd, 'wb', 0)) + + def test_read_fifo(self): + path = os_helper.TESTFN + os.mkfifo(path) + self.addCleanup(os_helper.unlink, path) + rfd = os.open(path, os.O_RDONLY | os.O_NONBLOCK) + wfd = os.open(path, os.O_WRONLY) + self.addCleanup(os.close, wfd) + self.check_accepted(self.loop.connect_read_pipe, open(rfd, 'rb', 0)) + + def test_write_fifo(self): + path = os_helper.TESTFN + os.mkfifo(path) + self.addCleanup(os_helper.unlink, path) + rfd = os.open(path, os.O_RDONLY | os.O_NONBLOCK) + self.addCleanup(os.close, rfd) + wfd = os.open(path, os.O_WRONLY) + self.check_accepted(self.loop.connect_write_pipe, open(wfd, 'wb', 0)) + + def test_read_socket(self): + rsock, wsock = socket.socketpair() + self.addCleanup(wsock.close) + self.check_accepted(self.loop.connect_read_pipe, + open(rsock.detach(), 'rb', 0)) + + def test_write_socket(self): + rsock, wsock = socket.socketpair() + self.addCleanup(rsock.close) + self.check_accepted(self.loop.connect_write_pipe, + open(wsock.detach(), 'wb', 0)) + + @unittest.skipUnless(hasattr(os, 'openpty'), 'need os.openpty()') + def test_read_character_device(self): + master, slave = os.openpty() + self.addCleanup(os.close, slave) + self.check_accepted(self.loop.connect_read_pipe, open(master, 'rb', 0)) + + @unittest.skipUnless(hasattr(os, 'openpty'), 'need os.openpty()') + def test_write_character_device(self): + master, slave = os.openpty() + self.addCleanup(os.close, master) + self.check_accepted(self.loop.connect_write_pipe, open(slave, 'wb', 0)) + + def test_read_regular_file(self): + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with open(os_helper.TESTFN, 'wb') as f: + f.write(b'spam') + self.check_rejected(self.loop.connect_read_pipe, + open(os_helper.TESTFN, 'rb', 0)) + + def test_write_regular_file(self): + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.check_rejected(self.loop.connect_write_pipe, + open(os_helper.TESTFN, 'wb', 0)) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index c40391b237d57e..8e7a7c406e425e 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -15,6 +15,8 @@ import asyncio from asyncio import windows_events +from asyncio import windows_utils +from test.support import os_helper from test.test_asyncio import utils as test_utils @@ -324,5 +326,89 @@ def threadMain(): thr.join() +class ProactorPipeObjectSupportTests(unittest.TestCase): + + def setUp(self): + super().setUp() + self.loop = asyncio.ProactorEventLoop() + self.addCleanup(self.loop.close) + self.errors = [] + self.loop.set_exception_handler( + lambda loop, context: self.errors.append(context)) + + def check_read_rejected(self, pipe): + self.addCleanup(pipe.close) + lost = self.loop.create_future() + + class Proto(asyncio.Protocol): + def connection_lost(self, exc): + if not lost.done(): + lost.set_result(exc) + + async def run(): + transport, _ = await self.loop.connect_read_pipe(Proto, pipe) + await lost + transport.close() + + self.loop.run_until_complete(run()) + self.assertTrue(self.errors, 'no error reached the exception handler') + self.assertIsInstance(self.errors[0]['exception'], OSError) + + def check_write_rejected(self, pipe): + self.addCleanup(pipe.close) + with self.assertRaises(OSError): + self.loop.run_until_complete( + self.loop.connect_write_pipe(asyncio.BaseProtocol, pipe)) + + def test_overlapped_pipe(self): + rhandle, whandle = windows_utils.pipe(overlapped=(True, True)) + rpipe = windows_utils.PipeHandle(rhandle) + wpipe = windows_utils.PipeHandle(whandle) + + chunks = [] + lost = self.loop.create_future() + + class ReadProto(asyncio.Protocol): + def data_received(self, data): + chunks.append(data) + + def connection_lost(self, exc): + if not lost.done(): + lost.set_result(exc) + + async def run(): + rtransport, _ = await self.loop.connect_read_pipe(ReadProto, rpipe) + wtransport, _ = await self.loop.connect_write_pipe( + asyncio.BaseProtocol, wpipe) + wtransport.write(b'spam') + wtransport.close() + await lost + rtransport.close() + + self.loop.run_until_complete(run()) + self.assertEqual(b''.join(chunks), b'spam') + self.assertFalse(self.errors) + + def test_read_non_overlapped_pipe(self): + rhandle, whandle = windows_utils.pipe(overlapped=(False, False)) + self.addCleanup(windows_utils.PipeHandle(whandle).close) + self.check_read_rejected(windows_utils.PipeHandle(rhandle)) + + def test_write_non_overlapped_pipe(self): + rhandle, whandle = windows_utils.pipe(overlapped=(False, False)) + self.addCleanup(windows_utils.PipeHandle(rhandle).close) + self.check_write_rejected(windows_utils.PipeHandle(whandle)) + + def test_read_regular_file(self): + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with open(os_helper.TESTFN, 'wb') as f: + f.write(b'spam') + self.check_read_rejected(open(os_helper.TESTFN, 'rb', 0)) + + def test_write_regular_file(self): + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.check_write_rejected(open(os_helper.TESTFN, 'wb', 0)) + + if __name__ == '__main__': unittest.main() From bb73164010e92fafaa2a755eb2872da01b336e2a Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 13 Jul 2026 16:42:00 +0530 Subject: [PATCH 3/4] more fixes --- Doc/library/asyncio-eventloop.rst | 9 ++++-- Doc/library/asyncio-platforms.rst | 8 ++--- Lib/asyncio/proactor_events.py | 6 +--- Lib/test/test_asyncio/test_windows_events.py | 34 +++++++++++++++----- 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index fb0fbb6e3b0415..6b5244abe427cd 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -1312,8 +1312,13 @@ following: * a character device, such as a terminal. On Windows, where only :class:`ProactorEventLoop` implements these methods, -*pipe* must wrap a handle opened for overlapped I/O, since the handle has to -be associated with an I/O completion port. +*pipe* must wrap a handle opened for overlapped I/O (that is, created with the +``FILE_FLAG_OVERLAPPED`` flag), since the handle has to be associated with an +I/O completion port. Handles that were not opened for overlapped I/O are +rejected. In particular, the standard streams (:data:`sys.stdin`, +:data:`sys.stdout` and :data:`sys.stderr`), console handles, and the pipes +created by :func:`os.pipe` are **not** opened for overlapped I/O and therefore +cannot be used with these methods. .. note:: diff --git a/Doc/library/asyncio-platforms.rst b/Doc/library/asyncio-platforms.rst index b8727f1bc4005e..0b7c17a2c58479 100644 --- a/Doc/library/asyncio-platforms.rst +++ b/Doc/library/asyncio-platforms.rst @@ -68,12 +68,8 @@ All event loops on Windows do not support the following methods: methods are not supported. * :meth:`loop.connect_read_pipe` and :meth:`loop.connect_write_pipe` only - accept a handle opened for overlapped I/O, which in practice means a named - pipe such as those created by :func:`!asyncio.windows_utils.pipe`. A - console handle cannot be associated with an I/O completion port, so - :data:`sys.stdin`, :data:`sys.stdout` and :data:`sys.stderr` are not - supported. See :ref:`Supported pipe objects ` for - how such a handle is rejected, which differs between the two methods. + accept a handle opened for overlapped I/O. + See :ref:`Supported pipe objects ` for which objects are supported. The resolution of the monotonic clock on Windows is usually around 15.6 milliseconds. The best resolution is 0.5 milliseconds. The resolution depends on the diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index cf2902b4c76559..f18a7fe5855815 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -62,6 +62,7 @@ def __init__(self, loop, sock, protocol, waiter=None, self._closing = False # Set when close() called. self._called_connection_lost = False self._eof_written = False + self._empty_waiter = None if self._server is not None: self._server._attach(self) self._loop.call_soon(self._protocol.connection_made, self) @@ -331,10 +332,6 @@ class _ProactorBaseWritePipeTransport(_ProactorBasePipeTransport, _start_tls_compatible = True - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - self._empty_waiter = None - def write(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError( @@ -465,7 +462,6 @@ class _ProactorDatagramTransport(_ProactorBasePipeTransport, def __init__(self, loop, sock, protocol, address=None, waiter=None, extra=None): self._address = address - self._empty_waiter = None self._buffer_size = 0 # We don't need to call _protocol.connection_made() since our base # constructor does it for us. diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 8e7a7c406e425e..0418379d3d9655 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -347,12 +347,12 @@ def connection_lost(self, exc): async def run(): transport, _ = await self.loop.connect_read_pipe(Proto, pipe) - await lost + exc = await lost transport.close() + return exc - self.loop.run_until_complete(run()) - self.assertTrue(self.errors, 'no error reached the exception handler') - self.assertIsInstance(self.errors[0]['exception'], OSError) + exc = self.loop.run_until_complete(run()) + self.assertIsInstance(exc, OSError) def check_write_rejected(self, pipe): self.addCleanup(pipe.close) @@ -360,10 +360,9 @@ def check_write_rejected(self, pipe): self.loop.run_until_complete( self.loop.connect_write_pipe(asyncio.BaseProtocol, pipe)) - def test_overlapped_pipe(self): - rhandle, whandle = windows_utils.pipe(overlapped=(True, True)) - rpipe = windows_utils.PipeHandle(rhandle) - wpipe = windows_utils.PipeHandle(whandle) + def check_accepted(self, rpipe, wpipe): + self.addCleanup(rpipe.close) + self.addCleanup(wpipe.close) chunks = [] lost = self.loop.create_future() @@ -389,6 +388,15 @@ async def run(): self.assertEqual(b''.join(chunks), b'spam') self.assertFalse(self.errors) + def test_overlapped_pipe(self): + rhandle, whandle = windows_utils.pipe(duplex=True, overlapped=(True, True)) + self.check_accepted(windows_utils.PipeHandle(rhandle), + windows_utils.PipeHandle(whandle)) + + def test_socketpair(self): + rsock, wsock = socket.socketpair() + self.check_accepted(rsock, wsock) + def test_read_non_overlapped_pipe(self): rhandle, whandle = windows_utils.pipe(overlapped=(False, False)) self.addCleanup(windows_utils.PipeHandle(whandle).close) @@ -409,6 +417,16 @@ def test_write_regular_file(self): self.addCleanup(os_helper.unlink, os_helper.TESTFN) self.check_write_rejected(open(os_helper.TESTFN, 'wb', 0)) + def test_read_os_pipe(self): + rfd, wfd = os.pipe() + self.addCleanup(os.close, wfd) + self.check_read_rejected(open(rfd, 'rb', 0)) + + def test_write_os_pipe(self): + rfd, wfd = os.pipe() + self.addCleanup(os.close, rfd) + self.check_write_rejected(open(wfd, 'wb', 0)) + if __name__ == '__main__': unittest.main() From 25316c984cf4421f1292b54feb5de767cfe54214 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 13 Jul 2026 17:08:27 +0530 Subject: [PATCH 4/4] ignore ResourceWarnings --- Lib/test/test_asyncio/test_windows_events.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py index 0418379d3d9655..c23427b8652069 100644 --- a/Lib/test/test_asyncio/test_windows_events.py +++ b/Lib/test/test_asyncio/test_windows_events.py @@ -5,6 +5,7 @@ import time import threading import unittest +import warnings from unittest import mock if sys.platform != 'win32': @@ -16,6 +17,7 @@ import asyncio from asyncio import windows_events from asyncio import windows_utils +from test import support from test.support import os_helper from test.test_asyncio import utils as test_utils @@ -356,9 +358,12 @@ async def run(): def check_write_rejected(self, pipe): self.addCleanup(pipe.close) - with self.assertRaises(OSError): - self.loop.run_until_complete( - self.loop.connect_write_pipe(asyncio.BaseProtocol, pipe)) + with warnings.catch_warnings(): + warnings.simplefilter('ignore', ResourceWarning) + with self.assertRaises(OSError): + self.loop.run_until_complete( + self.loop.connect_write_pipe(asyncio.BaseProtocol, pipe)) + support.gc_collect() def check_accepted(self, rpipe, wpipe): self.addCleanup(rpipe.close)