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. An host of '' or port 0 tells the OS to use the default.
""" """
msg = "getaddrinfo returns an empty list"
host, port = address host, port = address
err = None
for res in getaddrinfo(host, port, 0, SOCK_STREAM): for res in getaddrinfo(host, port, 0, SOCK_STREAM):
af, socktype, proto, canonname, sa = res af, socktype, proto, canonname, sa = res
sock = None sock = None
@ -311,9 +311,12 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
sock.connect(sa) sock.connect(sa)
return sock return sock
except error as err: except error as _:
msg = err err = _
if sock is not None: if sock is not None:
sock.close() 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 sys
import os import os
import array import array
import contextlib
from weakref import proxy from weakref import proxy
import signal import signal
@ -1203,12 +1204,42 @@ class BasicTCPTest2(NetworkConnectionTest, BasicTCPTest):
class NetworkConnectionNoServer(unittest.TestCase): 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() port = support.find_unused_port()
self.assertRaises( cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.error, with self.assertRaises(socket.error) as cm:
lambda: socket.create_connection((HOST, port)) 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.') @unittest.skipUnless(thread, 'Threading required for this test.')
class NetworkConnectionAttributesTest(SocketTCPTest, ThreadableTest): class NetworkConnectionAttributesTest(SocketTCPTest, ThreadableTest):

View File

@ -13,6 +13,11 @@ Core and Builtins
Library 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, - 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 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. be updated back after the system call. Original patch by Brian Brazil.