From 4b92b5fad3baaa22a3ab198556e1adf5a2df7d9c Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Tue, 7 Sep 2010 21:05:49 +0000 Subject: [PATCH] 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. --- Lib/socket.py | 11 +++++++---- Lib/test/test_socket.py | 41 ++++++++++++++++++++++++++++++++++++----- Misc/NEWS | 5 +++++ 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py index 89fe36e7f9a..004d6a9445f 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -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") diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 2f6d00756e6..19c494b127b 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -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): diff --git a/Misc/NEWS b/Misc/NEWS index 81df8036c5a..c0f2d6edee9 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -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.