From 7cd8f2bea39159c3a4ab4b4c71b98944519a559c Mon Sep 17 00:00:00 2001 From: "R. David Murray" Date: Wed, 15 Apr 2009 23:00:41 +0000 Subject: [PATCH] Merged revisions 70873,70904,70934,71490,71553,71579 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70873 | josiah.carlson | 2009-03-31 15:32:34 -0400 (Tue, 31 Mar 2009) | 2 lines This resolves issue 1161031. Tests pass. ........ r70904 | josiah.carlson | 2009-03-31 17:49:36 -0400 (Tue, 31 Mar 2009) | 3 lines Made handle_expt_event() be called last, so that we don't accidentally read after closing the socket. ........ r70934 | josiah.carlson | 2009-03-31 21:28:11 -0400 (Tue, 31 Mar 2009) | 2 lines Fix for failing asyncore tests. ........ r71490 | r.david.murray | 2009-04-11 13:52:56 -0400 (Sat, 11 Apr 2009) | 4 lines Make test_asyncore tests match code changes introduced by the fix to Issue1161031, refactoring the test to simplify it in the process. ........ r71553 | r.david.murray | 2009-04-12 21:06:46 -0400 (Sun, 12 Apr 2009) | 3 lines Adjust test_asyncore to account for intentional asyncore behavior change introduced by r70934 that was causing a test failure when run under -O. ........ r71579 | r.david.murray | 2009-04-13 12:56:32 -0400 (Mon, 13 Apr 2009) | 2 lines Add missing NEWS item for issue1161031 fix. ........ --- Doc/library/asyncore.rst | 3 +- Lib/asyncore.py | 52 +++++++++++++++---------------- Lib/test/test_asyncore.py | 65 ++++++++++++++------------------------- Misc/NEWS | 9 ++++++ 4 files changed, 59 insertions(+), 70 deletions(-) diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst index 4736a9c1110..a2e1b5ed73a 100644 --- a/Doc/library/asyncore.rst +++ b/Doc/library/asyncore.rst @@ -81,7 +81,8 @@ any that have been added to the map during asynchronous service) is closed. +----------------------+----------------------------------------+ | Event | Description | +======================+========================================+ - | ``handle_connect()`` | Implied by the first write event | + | ``handle_connect()`` | Implied by the first read or write | + | | event | +----------------------+----------------------------------------+ | ``handle_close()`` | Implied by a read event with no data | | | available | diff --git a/Lib/asyncore.py b/Lib/asyncore.py index 75fd5aedd2c..4f1153b760b 100644 --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -69,10 +69,12 @@ def _strerror(err): class ExitNow(Exception): pass +_reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit) + def read(obj): try: obj.handle_read_event() - except (ExitNow, KeyboardInterrupt, SystemExit): + except _reraised_exceptions: raise except: obj.handle_error() @@ -80,7 +82,7 @@ def read(obj): def write(obj): try: obj.handle_write_event() - except (ExitNow, KeyboardInterrupt, SystemExit): + except _reraised_exceptions: raise except: obj.handle_error() @@ -88,22 +90,22 @@ def write(obj): def _exception(obj): try: obj.handle_expt_event() - except (ExitNow, KeyboardInterrupt, SystemExit): + except _reraised_exceptions: raise except: obj.handle_error() def readwrite(obj, flags): try: - if flags & (select.POLLIN | select.POLLPRI): + if flags & select.POLLIN: obj.handle_read_event() if flags & select.POLLOUT: obj.handle_write_event() - if flags & (select.POLLERR | select.POLLNVAL): - obj.handle_expt_event() - if flags & select.POLLHUP: + if flags & (select.POLLHUP | select.POLLERR | select.POLLNVAL): obj.handle_close() - except (ExitNow, KeyboardInterrupt, SystemExit): + if flags & select.POLLPRI: + obj.handle_expt_event() + except _reraised_exceptions: raise except: obj.handle_error() @@ -211,6 +213,7 @@ class dispatcher: accepting = False closing = False addr = None + ignore_log_types = frozenset(['warning']) def __init__(self, sock=None, map=None): if map is None: @@ -398,7 +401,7 @@ class dispatcher: sys.stderr.write('log: %s\n' % str(message)) def log_info(self, message, type='info'): - if __debug__ or type != 'info': + if type not in self.ignore_log_types: print '%s: %s' % (type, message) def handle_read_event(self): @@ -432,22 +435,17 @@ class dispatcher: self.handle_write() def handle_expt_event(self): - # if the handle_expt is the same default worthless method, - # we'll not even bother calling it, we'll instead generate - # a useful error - x = True - try: - y1 = self.__class__.handle_expt.im_func - y2 = dispatcher.handle_expt.im_func - x = y1 is y2 - except AttributeError: - pass - - if x: - err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) - msg = _strerror(err) - - raise socket.error(err, msg) + # 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() @@ -472,7 +470,7 @@ class dispatcher: self.handle_close() def handle_expt(self): - self.log_info('unhandled exception', 'warning') + self.log_info('unhandled incoming priority event', 'warning') def handle_read(self): self.log_info('unhandled read event', 'warning') @@ -553,7 +551,7 @@ def close_all(map=None, ignore_all=False): pass elif not ignore_all: raise - except (ExitNow, KeyboardInterrupt, SystemExit): + except _reraised_exceptions: raise except: if not ignore_all: diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index 72de850f1de..ce835aa36c8 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -115,12 +115,24 @@ class HelperFunctionTests(unittest.TestCase): def test_readwrite(self): # Check that correct methods are called by readwrite() + attributes = ('read', 'expt', 'write', 'closed', 'error_handled') + + expected = ( + (select.POLLIN, 'read'), + (select.POLLPRI, 'expt'), + (select.POLLOUT, 'write'), + (select.POLLERR, 'closed'), + (select.POLLHUP, 'closed'), + (select.POLLNVAL, 'closed'), + ) + class testobj: def __init__(self): self.read = False self.write = False self.closed = False self.expt = False + self.error_handled = False def handle_read_event(self): self.read = True @@ -137,11 +149,15 @@ class HelperFunctionTests(unittest.TestCase): def handle_error(self): self.error_handled = True - for flag in (select.POLLIN, select.POLLPRI): + for flag, expectedattr in expected: tobj = testobj() - self.assertEqual(tobj.read, False) + self.assertEqual(getattr(tobj, expectedattr), False) asyncore.readwrite(tobj, flag) - self.assertEqual(tobj.read, True) + + # Only the attribute modified by the routine we expect to be + # called should be True. + for attr in attributes: + self.assertEqual(getattr(tobj, attr), attr==expectedattr) # check that ExitNow exceptions in the object handler method # bubbles all the way up through asyncore readwrite call @@ -151,40 +167,7 @@ class HelperFunctionTests(unittest.TestCase): # check that an exception other than ExitNow in the object handler # method causes the handle_error method to get called tr2 = crashingdummy() - asyncore.readwrite(tr2, flag) - self.assertEqual(tr2.error_handled, True) - - tobj = testobj() - self.assertEqual(tobj.write, False) - asyncore.readwrite(tobj, select.POLLOUT) - self.assertEqual(tobj.write, True) - - # check that ExitNow exceptions in the object handler method - # bubbles all the way up through asyncore readwrite call - tr1 = exitingdummy() - self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1, - select.POLLOUT) - - # check that an exception other than ExitNow in the object handler - # method causes the handle_error method to get called - tr2 = crashingdummy() - asyncore.readwrite(tr2, select.POLLOUT) - self.assertEqual(tr2.error_handled, True) - - for flag in (select.POLLERR, select.POLLHUP, select.POLLNVAL): - tobj = testobj() - self.assertEqual((tobj.expt, tobj.closed)[flag == select.POLLHUP], False) - asyncore.readwrite(tobj, flag) - self.assertEqual((tobj.expt, tobj.closed)[flag == select.POLLHUP], True) - - # check that ExitNow exceptions in the object handler method - # bubbles all the way up through asyncore readwrite calls - tr1 = exitingdummy() - self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1, flag) - - # check that an exception other than ExitNow in the object handler - # method causes the handle_error method to get called - tr2 = crashingdummy() + self.assertEqual(tr2.error_handled, False) asyncore.readwrite(tr2, flag) self.assertEqual(tr2.error_handled, True) @@ -289,15 +272,13 @@ class DispatcherTests(unittest.TestCase): sys.stdout = stdout lines = fp.getvalue().splitlines() - if __debug__: - expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3] - else: - expected = ['EGGS: %s' % l1, 'SPAM: %s' % l3] + expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3] self.assertEquals(lines, expected) def test_unhandled(self): d = asyncore.dispatcher() + d.ignore_log_types = () # capture output of dispatcher.log_info() (to stdout via print) fp = StringIO() @@ -313,7 +294,7 @@ class DispatcherTests(unittest.TestCase): sys.stdout = stdout lines = fp.getvalue().splitlines() - expected = ['warning: unhandled exception', + expected = ['warning: unhandled incoming priority event', 'warning: unhandled read event', 'warning: unhandled write event', 'warning: unhandled connect event', diff --git a/Misc/NEWS b/Misc/NEWS index 8cac11a4318..72158cfc750 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -14,6 +14,15 @@ Core and Builtins - Issue #5759: float() didn't call __float__ on str subclasses. +Library +------- + +- Issue #1161031: fix readwrite select flag handling: POLLPRI now + results in a handle_expt_event call, not handle_read_event, and POLLERR + and POLLNVAL now call handle_close, not handle_expt_event. Also, + dispatcher now has an 'ignore_log_types' attribute for suppressing + log messages, which is set to 'warning' by default. + What's New in Python 2.6.2 rc 1 ===============================