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.
This commit is contained in:
Antoine Pitrou 2010-09-07 21:05:49 +00:00
parent a88c83cbab
commit 4b92b5fad3
3 changed files with 48 additions and 9 deletions

View File

@ -297,8 +297,8 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
An host of '' or port 0 tells the OS to use the default.
"""
msg = "getaddrinfo returns an empty list"
host, port = address
err = None
for res in getaddrinfo(host, port, 0, SOCK_STREAM):
af, socktype, proto, canonname, sa = res
sock = None
@ -311,9 +311,12 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
sock.connect(sa)
return sock
except error as err:
msg = err
except error as _:
err = _
if sock is not None:
sock.close()
raise error(msg)
if err is not None:
raise err
else:
raise error("getaddrinfo returns an empty list")

View File

@ -13,6 +13,7 @@ import queue
import sys
import os
import array
import contextlib
from weakref import proxy
import signal
@ -1203,12 +1204,42 @@ class BasicTCPTest2(NetworkConnectionTest, BasicTCPTest):
class NetworkConnectionNoServer(unittest.TestCase):
def testWithoutServer(self):
class MockSocket(socket.socket):
def connect(self, *args):
raise socket.timeout('timed out')
@contextlib.contextmanager
def mocked_socket_module(self):
"""Return a socket which times out on connect"""
old_socket = socket.socket
socket.socket = self.MockSocket
try:
yield
finally:
socket.socket = old_socket
def test_connect(self):
port = support.find_unused_port()
self.assertRaises(
socket.error,
lambda: socket.create_connection((HOST, port))
)
cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
with self.assertRaises(socket.error) as cm:
cli.connect((HOST, port))
self.assertEqual(cm.exception.errno, errno.ECONNREFUSED)
def test_create_connection(self):
# Issue #9792: errors raised by create_connection() should have
# a proper errno attribute.
port = support.find_unused_port()
with self.assertRaises(socket.error) as cm:
socket.create_connection((HOST, port))
self.assertEqual(cm.exception.errno, errno.ECONNREFUSED)
def test_create_connection_timeout(self):
# Issue #9792: create_connection() should not recast timeout errors
# as generic socket errors.
with self.mocked_socket_module():
with self.assertRaises(socket.timeout):
socket.create_connection((HOST, 1234))
@unittest.skipUnless(thread, 'Threading required for this test.')
class NetworkConnectionAttributesTest(SocketTCPTest, ThreadableTest):

View File

@ -13,6 +13,11 @@ Core and Builtins
Library
-------
- 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.
- Issue #9758: When fcntl.ioctl() was called with mutable_flag set to True,
and the passed buffer was exactly 1024 bytes long, the buffer wouldn't
be updated back after the system call. Original patch by Brian Brazil.