Skip to content

Commit 4b92b5f

Browse files
committed
Issue #9792: In case of connection failure, socket.create_connection()
would swallow the exception and raise a new one, making it impossible to fetch the original errno, or to filter timeout errors. Now the original error is re-raised.
1 parent a88c83c commit 4b92b5f

3 files changed

Lines changed: 48 additions & 9 deletions

File tree

Lib/socket.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,8 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
297297
An host of '' or port 0 tells the OS to use the default.
298298
"""
299299

300-
msg = "getaddrinfo returns an empty list"
301300
host, port = address
301+
err = None
302302
for res in getaddrinfo(host, port, 0, SOCK_STREAM):
303303
af, socktype, proto, canonname, sa = res
304304
sock = None
@@ -311,9 +311,12 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
311311
sock.connect(sa)
312312
return sock
313313

314-
except error as err:
315-
msg = err
314+
except error as _:
315+
err = _
316316
if sock is not None:
317317
sock.close()
318318

319-
raise error(msg)
319+
if err is not None:
320+
raise err
321+
else:
322+
raise error("getaddrinfo returns an empty list")

Lib/test/test_socket.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import sys
1414
import os
1515
import array
16+
import contextlib
1617
from weakref import proxy
1718
import signal
1819

@@ -1203,12 +1204,42 @@ class BasicTCPTest2(NetworkConnectionTest, BasicTCPTest):
12031204

12041205
class NetworkConnectionNoServer(unittest.TestCase):
12051206

1206-
def testWithoutServer(self):
1207+
class MockSocket(socket.socket):
1208+
def connect(self, *args):
1209+
raise socket.timeout('timed out')
1210+
1211+
@contextlib.contextmanager
1212+
def mocked_socket_module(self):
1213+
"""Return a socket which times out on connect"""
1214+
old_socket = socket.socket
1215+
socket.socket = self.MockSocket
1216+
try:
1217+
yield
1218+
finally:
1219+
socket.socket = old_socket
1220+
1221+
def test_connect(self):
12071222
port = support.find_unused_port()
1208-
self.assertRaises(
1209-
socket.error,
1210-
lambda: socket.create_connection((HOST, port))
1211-
)
1223+
cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1224+
with self.assertRaises(socket.error) as cm:
1225+
cli.connect((HOST, port))
1226+
self.assertEqual(cm.exception.errno, errno.ECONNREFUSED)
1227+
1228+
def test_create_connection(self):
1229+
# Issue #9792: errors raised by create_connection() should have
1230+
# a proper errno attribute.
1231+
port = support.find_unused_port()
1232+
with self.assertRaises(socket.error) as cm:
1233+
socket.create_connection((HOST, port))
1234+
self.assertEqual(cm.exception.errno, errno.ECONNREFUSED)
1235+
1236+
def test_create_connection_timeout(self):
1237+
# Issue #9792: create_connection() should not recast timeout errors
1238+
# as generic socket errors.
1239+
with self.mocked_socket_module():
1240+
with self.assertRaises(socket.timeout):
1241+
socket.create_connection((HOST, 1234))
1242+
12121243

12131244
@unittest.skipUnless(thread, 'Threading required for this test.')
12141245
class NetworkConnectionAttributesTest(SocketTCPTest, ThreadableTest):

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ Core and Builtins
1313
Library
1414
-------
1515

16+
- Issue #9792: In case of connection failure, socket.create_connection()
17+
would swallow the exception and raise a new one, making it impossible
18+
to fetch the original errno, or to filter timeout errors. Now the
19+
original error is re-raised.
20+
1621
- Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True,
1722
and the passed buffer was exactly 1024 bytes long, the buffer wouldn't
1823
be updated back after the system call. Original patch by Brian Brazil.

0 commit comments

Comments
 (0)