Index: Lib/asyncore.py =================================================================== --- Lib/asyncore.py (revision 74225) +++ Lib/asyncore.py (working copy) @@ -89,7 +89,15 @@ def _exception(obj): try: - obj.handle_expt_event() + try: + err = obj.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + except AttributeError: + # This handles the case where obj isn't a socket dispatcher. + pass + else: + if err != 0: + raise socket.error(err, _strerror(err)) + obj.handle_close_event() except _reraised_exceptions: raise except: @@ -415,6 +423,11 @@ # sockets that are connected self.handle_accept() elif not self.connected: + # check for errors + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + raise socket.error(err, _strerror(err)) + self.handle_connect_event() self.handle_read() else: @@ -431,28 +444,17 @@ return if not self.connected: - #check for errors + # check for errors err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) if err != 0: raise socket.error(err, _strerror(err)) self.handle_connect_event() + self.handle_write() def handle_expt_event(self): - # handle_expt_event() is called if there might be an error on the - # socket, or if there is OOB data - # check for the error condition first - err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) - if err != 0: - # we can get here when select.select() says that there is an - # exceptional condition on the socket - # since there is an error, we'll go ahead and close the socket - # like we would in a subclassed handle_read() that received no - # data - self.handle_close() - else: - self.handle_expt() + self.handle_expt() def handle_error(self): nil, t, v, tbinfo = compact_traceback() Index: Lib/test/test_asyncore.py =================================================================== --- Lib/test/test_asyncore.py (revision 74225) +++ Lib/test/test_asyncore.py (working copy) @@ -1,8 +1,10 @@ import asyncore +import asynchat import unittest import select import os import socket +import errno import threading import sys import time @@ -39,7 +41,8 @@ handle_write_event = handle_read_event handle_close = handle_read_event - handle_expt_event = handle_read_event + handle_close_event = handle_read_event + handle_error = handle_read_event class crashingdummy: def __init__(self): @@ -50,7 +53,7 @@ handle_write_event = handle_read_event handle_close = handle_read_event - handle_expt_event = handle_read_event + handle_close_event = handle_read_event def handle_error(self): self.error_handled = True @@ -301,8 +304,62 @@ 'warning: unhandled accept event'] self.assertEquals(lines, expected) +# this stuff tests disconnection/connection refused, as well as some +# asyncore/asynchat integration +class testing_channel(asynchat.async_chat): + def __init__(self, *a, **kw): + asynchat.async_chat.__init__(self, *a, **kw) + self.exceptions = [] + self.close_count = 0 + def collect_incoming_data(self, data): + pass + def found_terminator(self): + pass + + def handle_connect(self): + self.push("connected") + + def handle_error(self): + self.exceptions.append(sys.exc_info()[1]) + self.handle_close() + + def handle_close_event(self): + self.handle_close() + + def handle_close(self): + self.close_count += 1 + self.close() + + +class ConnectionRefusedTests(unittest.TestCase): + + def setUp(self): + s = socket.socket() + self.channel = testing_channel(s) + + # Assumes that noone is listeing here + self.channel.connect(('localhost', 65535)) + + # give time for socket to connect + time.sleep(0.1) + + asyncore.loop(count=20) + + def tearDown(self): + asyncore.close_all() + + def test_single_exception(self): + self.assertEqual(len(self.channel.exceptions), 1) + + def test_connection_refused(self): + self.assertEqual(self.channel.exceptions[0].args[0], errno.ECONNREFUSED) + + def test_close_once(self): + self.assertEqual(self.channel.close_count, 1) + + class dispatcherwithsend_noread(asyncore.dispatcher_with_send): def readable(self): return False @@ -393,8 +450,8 @@ def test_main(): - tests = [HelperFunctionTests, DispatcherTests, DispatcherWithSendTests, - DispatcherWithSendTests_UsePoll] + tests = [HelperFunctionTests, DispatcherTests, ConnectionRefusedTests, + DispatcherWithSendTests, DispatcherWithSendTests_UsePoll] if hasattr(asyncore, 'file_wrapper'): tests.append(FileWrapperTest)