https://github.com/python/cpython/commit/9084b151567d02936ea1374961809b69b4cd883d
commit: 9084b151567d02936ea1374961809b69b4cd883d
branch: main
author: Serhiy Storchaka <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2025-07-03T09:38:39+05:30
summary:
gh-135836: Fix `IndexError` in `asyncio.create_connection()` (#135875)
files:
A Misc/NEWS.d/next/Library/2025-06-24-10-52-35.gh-issue-135836.s37351.rst
M Lib/asyncio/base_events.py
M Lib/test/test_asyncio/test_base_events.py
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index 2ff9e4017bb245..520d4b398545bf 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -1016,38 +1016,43 @@ async def _connect_sock(self, exceptions, addr_info,
local_addr_infos=None):
family, type_, proto, _, address = addr_info
sock = None
try:
- sock = socket.socket(family=family, type=type_, proto=proto)
- sock.setblocking(False)
- if local_addr_infos is not None:
- for lfamily, _, _, _, laddr in local_addr_infos:
- # skip local addresses of different family
- if lfamily != family:
- continue
- try:
- sock.bind(laddr)
- break
- except OSError as exc:
- msg = (
- f'error while attempting to bind on '
- f'address {laddr!r}: {str(exc).lower()}'
- )
- exc = OSError(exc.errno, msg)
- my_exceptions.append(exc)
- else: # all bind attempts failed
- if my_exceptions:
- raise my_exceptions.pop()
- else:
- raise OSError(f"no matching local address with
{family=} found")
- await self.sock_connect(sock, address)
- return sock
- except OSError as exc:
- my_exceptions.append(exc)
- if sock is not None:
- sock.close()
- raise
+ try:
+ sock = socket.socket(family=family, type=type_, proto=proto)
+ sock.setblocking(False)
+ if local_addr_infos is not None:
+ for lfamily, _, _, _, laddr in local_addr_infos:
+ # skip local addresses of different family
+ if lfamily != family:
+ continue
+ try:
+ sock.bind(laddr)
+ break
+ except OSError as exc:
+ msg = (
+ f'error while attempting to bind on '
+ f'address {laddr!r}: {str(exc).lower()}'
+ )
+ exc = OSError(exc.errno, msg)
+ my_exceptions.append(exc)
+ else: # all bind attempts failed
+ if my_exceptions:
+ raise my_exceptions.pop()
+ else:
+ raise OSError(f"no matching local address with
{family=} found")
+ await self.sock_connect(sock, address)
+ return sock
+ except OSError as exc:
+ my_exceptions.append(exc)
+ raise
except:
if sock is not None:
- sock.close()
+ try:
+ sock.close()
+ except OSError:
+ # An error when closing a newly created socket is
+ # not important, but it can overwrite more important
+ # non-OSError error. So ignore it.
+ pass
raise
finally:
exceptions = my_exceptions = None
diff --git a/Lib/test/test_asyncio/test_base_events.py
b/Lib/test/test_asyncio/test_base_events.py
index bb9f366fc411aa..12179eb0c9e274 100644
--- a/Lib/test/test_asyncio/test_base_events.py
+++ b/Lib/test/test_asyncio/test_base_events.py
@@ -24,6 +24,10 @@
MOCK_ANY = mock.ANY
+class CustomError(Exception):
+ pass
+
+
def tearDownModule():
asyncio._set_event_loop_policy(None)
@@ -1326,6 +1330,31 @@ def getaddrinfo_task(*args, **kwds):
self.assertEqual(len(cm.exception.exceptions), 1)
self.assertIsInstance(cm.exception.exceptions[0], OSError)
+ @patch_socket
+ def test_create_connection_connect_non_os_err_close_err(self, m_socket):
+ # Test the case when sock_connect() raises non-OSError exception
+ # and sock.close() raises OSError.
+ async def getaddrinfo(*args, **kw):
+ return [(2, 1, 6, '', ('107.6.106.82', 80))]
+
+ def getaddrinfo_task(*args, **kwds):
+ return self.loop.create_task(getaddrinfo(*args, **kwds))
+
+ self.loop.getaddrinfo = getaddrinfo_task
+ self.loop.sock_connect = mock.Mock()
+ self.loop.sock_connect.side_effect = CustomError
+ sock = mock.Mock()
+ m_socket.socket.return_value = sock
+ sock.close.side_effect = OSError
+
+ coro = self.loop.create_connection(MyProto, 'example.com', 80)
+ self.assertRaises(
+ CustomError, self.loop.run_until_complete, coro)
+
+ coro = self.loop.create_connection(MyProto, 'example.com', 80,
all_errors=True)
+ self.assertRaises(
+ CustomError, self.loop.run_until_complete, coro)
+
def test_create_connection_multiple(self):
async def getaddrinfo(*args, **kw):
return [(2, 1, 6, '', ('0.0.0.1', 80)),
diff --git
a/Misc/NEWS.d/next/Library/2025-06-24-10-52-35.gh-issue-135836.s37351.rst
b/Misc/NEWS.d/next/Library/2025-06-24-10-52-35.gh-issue-135836.s37351.rst
new file mode 100644
index 00000000000000..1d1e7a2298c085
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-06-24-10-52-35.gh-issue-135836.s37351.rst
@@ -0,0 +1,3 @@
+Fix :exc:`IndexError` in :meth:`asyncio.loop.create_connection` that could
+occur when non-\ :exc:`OSError` exception is raised during connection and
+socket's ``close()`` raises :exc:`!OSError`.
_______________________________________________
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]