# HG changeset patch # Parent 86b59ac81111ab4a2ebaeb9215f5e45ab38f305e diff -r 86b59ac81111 Doc/library/socketserver.rst --- a/Doc/library/socketserver.rst Mon Feb 09 08:10:32 2015 +0100 +++ b/Doc/library/socketserver.rst Sun Feb 15 05:06:25 2015 +0000 @@ -260,6 +260,9 @@ method raises an exception. The default action is to print the traceback to standard output and continue handling further requests. + .. versionchanged:: 3.5 + Now only called for exceptions derived from the :exc:`Exception` class. + .. method:: BaseServer.handle_timeout() diff -r 86b59ac81111 Lib/socketserver.py --- a/Lib/socketserver.py Mon Feb 09 08:10:32 2015 +0100 +++ b/Lib/socketserver.py Sun Feb 15 05:06:25 2015 +0000 @@ -132,6 +132,7 @@ import selectors import os import errno +import sys try: import threading except ImportError: @@ -316,9 +317,12 @@ if self.verify_request(request, client_address): try: self.process_request(request, client_address) - except: + except Exception: self.handle_error(request, client_address) self.shutdown_request(request) + except: + self.shutdown_request(request) + raise def handle_timeout(self): """Called if no new request arrives within self.timeout. @@ -370,12 +374,12 @@ The default is to print a traceback and continue. """ - print('-'*40) - print('Exception happened during processing of request from', end=' ') - print(client_address) + print('-'*40, file=sys.stderr) + print('Exception happened during processing of request from', + client_address, file=sys.stderr) import traceback - traceback.print_exc() # XXX But this goes to stderr! - print('-'*40) + traceback.print_exc() + print('-'*40, file=sys.stderr) class TCPServer(BaseServer): @@ -603,9 +607,10 @@ self.finish_request(request, client_address) self.shutdown_request(request) os._exit(0) - except: + except BaseException as exc: try: - self.handle_error(request, client_address) + if isinstance(exc, Exception): + self.handle_error(request, client_address) self.shutdown_request(request) finally: os._exit(1) @@ -626,9 +631,9 @@ """ try: self.finish_request(request, client_address) - self.shutdown_request(request) - except: + except Exception: self.handle_error(request, client_address) + finally: self.shutdown_request(request) def process_request(self, request, client_address): diff -r 86b59ac81111 Lib/test/test_socketserver.py --- a/Lib/test/test_socketserver.py Mon Feb 09 08:10:32 2015 +0100 +++ b/Lib/test/test_socketserver.py Sun Feb 15 05:06:25 2015 +0000 @@ -15,6 +15,7 @@ import test.support from test.support import reap_children, reap_threads, verbose +from test.script_helper import assert_python_ok try: import threading except ImportError: @@ -279,6 +280,85 @@ socketserver.TCPServer((HOST, -1), socketserver.StreamRequestHandler) + def test_error_handler(self): + # Should catch normal exceptions from the hander, but not exiting + # exceptions like SystemExit and KeyboardInterrpt + class BadHandler(socketserver.BaseRequestHandler): + def handle(self): + self.server.handler_called = True + raise ValueError('Test error') + + class ExitingHandler(socketserver.BaseRequestHandler): + def handle(self): + self.server.handler_called = True + raise SystemExit() + + class SimpleServer(socketserver.BaseServer): + def __init__(self, RequestHandlerClass): + self.error_handled = False + self.socket = DummySocket() + super().__init__(None, RequestHandlerClass) + + def get_request(self): + return (None, 'Dummy client') + + def handle_error(self, *pos, **kw): + self.error_handled = True + + class DummySocket: + def gettimeout(self): + return None + + # Single threaded server + server = SimpleServer(BadHandler) + server._handle_request_noblock() + self.assertTrue(server.handler_called) + self.assertTrue(server.error_handled) + server = SimpleServer(ExitingHandler) + self.assertRaises(SystemExit, server._handle_request_noblock) + self.assertTrue(server.handler_called) + self.assertFalse(server.error_handled) + + class ThreadingServer(socketserver.ThreadingMixIn, SimpleServer): + pass + server = ThreadingServer(BadHandler) + server._handle_request_noblock() + self.assertTrue(server.handler_called) + self.assertTrue(server.error_handled) + server = ThreadingServer(ExitingHandler) + server._handle_request_noblock() + self.assertTrue(server.handler_called) + self.assertFalse(server.error_handled) + + @requires_forking + def test_forking_error_handler(self): + _, stdout, stderr = assert_python_ok('-c', + 'from socketserver import BaseServer, ForkingMixIn\n' + 'from socketserver import BaseRequestHandler\n' + 'from sys import stderr\n' + 'class Server(ForkingMixIn, BaseServer):\n' + ' def get_request(self):\n' + ' return (None, "Dummy client")\n' + ' def handle_error(self, *pos, **kw):\n' + ' super().handle_error(*pos, **kw)\n' + ' stderr.flush() # Process will exit soon\n' + 'class BadHandler(BaseRequestHandler):\n' + ' def handle(self):\n' + ' raise ValueError("Test error")\n' + 'server = Server(None, BadHandler)\n' + 'server._handle_request_noblock()\n' + 'class ExitingHandler(BaseRequestHandler):\n' + ' def handle(self):\n' + ' raise SystemExit()\n' + 'server = Server(None, ExitingHandler)\n' + 'server._handle_request_noblock()\n' + 'print("Reached end")\n' + ) + self.assertEqual(stdout, b'Reached end\n') + self.assertIn(b'ValueError', stderr) + self.assertIn(b'Test error', stderr) + self.assertNotIn(b'SystemExit', stderr) + class MiscTestCase(unittest.TestCase):