https://github.com/python/cpython/commit/7a1d2c462bb544eb3de7f04d1fc5d09a327f301c
commit: 7a1d2c462bb544eb3de7f04d1fc5d09a327f301c
branch: main
author: lpyuu <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-09-07T16:40:24+05:30
summary:

gh-157025: Fix transport cleanup when cancelling asyncio native sendfile 
(#157027)

files:
A Misc/NEWS.d/next/Library/2026-09-06-15-12-18.gh-issue-157025.ZvFpFT.rst
M Lib/asyncio/proactor_events.py
M Lib/asyncio/selector_events.py
M Lib/test/test_asyncio/test_sendfile.py

diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py
index 7adb09f3fce5de7..6717f067caa08bb 100644
--- a/Lib/asyncio/proactor_events.py
+++ b/Lib/asyncio/proactor_events.py
@@ -766,8 +766,8 @@ async def _sock_sendfile_native(self, sock, file, offset, 
count):
     async def _sendfile_native(self, transp, file, offset, count):
         resume_reading = transp.is_reading()
         transp.pause_reading()
-        await transp._make_empty_waiter()
         try:
+            await transp._make_empty_waiter()
             return await self.sock_sendfile(transp._sock, file, offset, count,
                                             fallback=False)
         finally:
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
index a682063d9d3d6e3..bf20f053ded4353 100644
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -739,8 +739,8 @@ async def _sendfile_native(self, transp, file, offset, 
count):
         del self._transports[transp._sock_fd]
         resume_reading = transp.is_reading()
         transp.pause_reading()
-        await transp._make_empty_waiter()
         try:
+            await transp._make_empty_waiter()
             return await self.sock_sendfile(transp._sock, file, offset, count,
                                             fallback=False)
         finally:
diff --git a/Lib/test/test_asyncio/test_sendfile.py 
b/Lib/test/test_asyncio/test_sendfile.py
index c8d429c3d1651f0..91bbbc2e6993dae 100644
--- a/Lib/test/test_asyncio/test_sendfile.py
+++ b/Lib/test/test_asyncio/test_sendfile.py
@@ -376,6 +376,47 @@ def test_sendfile(self):
         self.assertEqual(srv_proto.data, self.DATA)
         self.assertEqual(self.file.tell(), len(self.DATA))
 
+    def test_sendfile_cancel_empty_waiter(self):
+        for reading in (True, False):
+            with self.subTest(reading=reading):
+                srv_proto, cli_proto = self.prepare_sendfile()
+                transport = cli_proto.transport
+                if not reading:
+                    transport.pause_reading()
+                waiter = self.loop.create_future()
+
+                def make_empty_waiter():
+                    transport._empty_waiter = waiter
+                    return waiter
+
+                with mock.patch.object(transport, '_make_empty_waiter',
+                                       side_effect=make_empty_waiter):
+                    task = self.loop.create_task(
+                        self.loop.sendfile(transport, self.file))
+                    test_utils.run_briefly(self.loop)
+                    self.assertIs(transport._empty_waiter, waiter)
+                    self.assertFalse(waiter.done())
+                    self.assertFalse(transport.is_reading())
+                    task.cancel()
+                    with self.assertRaises(asyncio.CancelledError):
+                        self.run_loop(task)
+
+                try:
+                    self.assertIsNone(transport._empty_waiter)
+                    self.assertEqual(transport.is_reading(), reading)
+                    if isinstance(self.loop, asyncio.SelectorEventLoop):
+                        self.assertIs(
+                            self.loop._transports[transport._sock_fd],
+                            transport)
+                finally:
+                    transport._reset_empty_waiter()
+
+                ret = self.run_loop(self.loop.sendfile(transport, self.file))
+                transport.close()
+                self.run_loop(srv_proto.done)
+                self.assertEqual(ret, len(self.DATA))
+                self.assertEqual(srv_proto.data, self.DATA)
+
     def test_sendfile_force_fallback(self):
         srv_proto, cli_proto = self.prepare_sendfile()
 
diff --git 
a/Misc/NEWS.d/next/Library/2026-09-06-15-12-18.gh-issue-157025.ZvFpFT.rst 
b/Misc/NEWS.d/next/Library/2026-09-06-15-12-18.gh-issue-157025.ZvFpFT.rst
new file mode 100644
index 000000000000000..fd1a6d7c5f47527
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-09-06-15-12-18.gh-issue-157025.ZvFpFT.rst
@@ -0,0 +1,2 @@
+Fix transport cleanup when cancelling :meth:`asyncio.loop.sendfile` while
+waiting for the write buffer to drain in the native implementation.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to