diff -r 5ffebeb3e91d Doc/library/http.server.rst --- a/Doc/library/http.server.rst Tue Feb 23 22:10:06 2016 +1100 +++ b/Doc/library/http.server.rst Wed Feb 24 11:00:00 2016 +0200 @@ -375,10 +375,9 @@ Handler = http.server.SimpleHTTPRequestHandler - httpd = socketserver.TCPServer(("", PORT), Handler) - - print("serving at port", PORT) - httpd.serve_forever() + with socketserver.TCPServer(("", PORT), Handler) as httpd: + print("serving at port", PORT) + httpd.serve_forever() .. _http-server-cli: diff -r 5ffebeb3e91d Doc/library/socketserver.rst --- a/Doc/library/socketserver.rst Tue Feb 23 22:10:06 2016 +1100 +++ b/Doc/library/socketserver.rst Wed Feb 24 11:00:00 2016 +0200 @@ -52,11 +52,12 @@ overriding its :meth:`~BaseRequestHandler.handle` method; this method will process incoming requests. Second, you must instantiate one of the server classes, passing it -the server's address and the request handler class. Then call the +the server's address and the request handler class. It is recommended to use +the server in a :keyword:`with` statement. Then call the :meth:`~BaseServer.handle_request` or :meth:`~BaseServer.serve_forever` method of the server object to process one or many requests. Finally, call :meth:`~BaseServer.server_close` -to close the socket. +to close the socket (unless you used the :keyword:`with` statement). When inheriting from :class:`ThreadingMixIn` for threaded connection behavior, you should explicitly declare how you want your threads to behave on an abrupt @@ -353,6 +354,11 @@ default implementation always returns :const:`True`. + .. versionadded:: 3.6 + Support for the :term:`context manager` protocol was added. Exiting the + context manager is equivalent to calling :meth:`server_close`. + + Request Handler Objects ----------------------- @@ -433,11 +439,10 @@ HOST, PORT = "localhost", 9999 # Create the server, binding to localhost on port 9999 - server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) - - # Activate the server; this will keep running until you - # interrupt the program with Ctrl-C - server.serve_forever() + with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server: + # Activate the server; this will keep running until you + # interrupt the program with Ctrl-C + server.serve_forever() An alternative request handler class that makes use of streams (file-like objects that simplify communication by providing the standard file interface):: @@ -529,8 +534,8 @@ if __name__ == "__main__": HOST, PORT = "localhost", 9999 - server = socketserver.UDPServer((HOST, PORT), MyUDPHandler) - server.serve_forever() + with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server: + server.serve_forever() This is the client side:: @@ -591,23 +596,22 @@ # Port 0 means to select an arbitrary unused port HOST, PORT = "localhost", 0 - server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler) - ip, port = server.server_address + with ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler) as server: + ip, port = server.server_address - # Start a thread with the server -- that thread will then start one - # more thread for each request - server_thread = threading.Thread(target=server.serve_forever) - # Exit the server thread when the main thread terminates - server_thread.daemon = True - server_thread.start() - print("Server loop running in thread:", server_thread.name) + # Start a thread with the server -- that thread will then start one + # more thread for each request + server_thread = threading.Thread(target=server.serve_forever) + # Exit the server thread when the main thread terminates + server_thread.daemon = True + server_thread.start() + print("Server loop running in thread:", server_thread.name) - client(ip, port, "Hello World 1") - client(ip, port, "Hello World 2") - client(ip, port, "Hello World 3") + client(ip, port, "Hello World 1") + client(ip, port, "Hello World 2") + client(ip, port, "Hello World 3") - server.shutdown() - server.server_close() + server.shutdown() The output of the example should look something like this:: diff -r 5ffebeb3e91d Doc/library/wsgiref.rst --- a/Doc/library/wsgiref.rst Tue Feb 23 22:10:06 2016 +1100 +++ b/Doc/library/wsgiref.rst Wed Feb 24 11:00:00 2016 +0200 @@ -131,9 +131,9 @@ for key, value in environ.items()] return ret - httpd = make_server('', 8000, simple_app) - print("Serving on port 8000...") - httpd.serve_forever() + with make_server('', 8000, simple_app) as httpd: + print("Serving on port 8000...") + httpd.serve_forever() In addition to the environment functions above, the :mod:`wsgiref.util` module @@ -283,14 +283,14 @@ from wsgiref.simple_server import make_server, demo_app - httpd = make_server('', 8000, demo_app) - print("Serving HTTP on port 8000...") + with make_server('', 8000, demo_app) as httpd: + print("Serving HTTP on port 8000...") - # Respond to requests until process is killed - httpd.serve_forever() + # Respond to requests until process is killed + httpd.serve_forever() - # Alternative: serve one request, then exit - httpd.handle_request() + # Alternative: serve one request, then exit + httpd.handle_request() .. function:: demo_app(environ, start_response) @@ -430,9 +430,9 @@ # This is the application wrapped in a validator validator_app = validator(simple_app) - httpd = make_server('', 8000, validator_app) - print("Listening on port 8000....") - httpd.serve_forever() + with make_server('', 8000, validator_app) as httpd: + print("Listening on port 8000....") + httpd.serve_forever() :mod:`wsgiref.handlers` -- server/gateway base classes @@ -769,8 +769,8 @@ # The returned object is going to be printed return [b"Hello World"] - httpd = make_server('', 8000, hello_world_app) - print("Serving on port 8000...") + with make_server('', 8000, hello_world_app) as httpd: + print("Serving on port 8000...") - # Serve until process is killed - httpd.serve_forever() + # Serve until process is killed + httpd.serve_forever() diff -r 5ffebeb3e91d Doc/library/xmlrpc.server.rst --- a/Doc/library/xmlrpc.server.rst Tue Feb 23 22:10:06 2016 +1100 +++ b/Doc/library/xmlrpc.server.rst Wed Feb 24 11:00:00 2016 +0200 @@ -147,29 +147,29 @@ rpc_paths = ('/RPC2',) # Create server - server = SimpleXMLRPCServer(("localhost", 8000), - requestHandler=RequestHandler) - server.register_introspection_functions() + with SimpleXMLRPCServer(("localhost", 8000), + requestHandler=RequestHandler) as server: + server.register_introspection_functions() - # Register pow() function; this will use the value of - # pow.__name__ as the name, which is just 'pow'. - server.register_function(pow) + # Register pow() function; this will use the value of + # pow.__name__ as the name, which is just 'pow'. + server.register_function(pow) - # Register a function under a different name - def adder_function(x,y): - return x + y - server.register_function(adder_function, 'add') + # Register a function under a different name + def adder_function(x,y): + return x + y + server.register_function(adder_function, 'add') - # Register an instance; all the methods of the instance are - # published as XML-RPC methods (in this case, just 'mul'). - class MyFuncs: - def mul(self, x, y): - return x * y + # Register an instance; all the methods of the instance are + # published as XML-RPC methods (in this case, just 'mul'). + class MyFuncs: + def mul(self, x, y): + return x * y - server.register_instance(MyFuncs()) + server.register_instance(MyFuncs()) - # Run the server's main loop - server.serve_forever() + # Run the server's main loop + server.serve_forever() The following client code will call the methods made available by the preceding server:: @@ -206,18 +206,17 @@ def getCurrentTime(): return datetime.datetime.now() - server = SimpleXMLRPCServer(("localhost", 8000)) - server.register_function(pow) - server.register_function(lambda x,y: x+y, 'add') - server.register_instance(ExampleService(), allow_dotted_names=True) - server.register_multicall_functions() - print('Serving XML-RPC on localhost port 8000') - try: - server.serve_forever() - except KeyboardInterrupt: - print("\nKeyboard interrupt received, exiting.") - server.server_close() - sys.exit(0) + with SimpleXMLRPCServer(("localhost", 8000)) as server: + server.register_function(pow) + server.register_function(lambda x,y: x+y, 'add') + server.register_instance(ExampleService(), allow_dotted_names=True) + server.register_multicall_functions() + print('Serving XML-RPC on localhost port 8000') + try: + server.serve_forever() + except KeyboardInterrupt: + print("\nKeyboard interrupt received, exiting.") + sys.exit(0) This ExampleService demo can be invoked from the command line:: diff -r 5ffebeb3e91d Doc/whatsnew/3.6.rst --- a/Doc/whatsnew/3.6.rst Tue Feb 23 22:10:06 2016 +1100 +++ b/Doc/whatsnew/3.6.rst Wed Feb 24 11:00:00 2016 +0200 @@ -186,6 +186,16 @@ (Contributed by Aviv Palivoda in :issue:`26243`.) +socketserver +---- + +Servers based on the :mod:`socketserver` module, including those +defined in :mod:`http.server`, :mod:`xmlrpc.server` and +:mod:`wsgiref.simple_server`, supports the :term:`context manager` +protocol. +(Contributed by Aviv Palivoda in :issue:`26404`.) + + Optimizations ============= diff -r 5ffebeb3e91d Lib/http/server.py --- a/Lib/http/server.py Tue Feb 23 22:10:06 2016 +1100 +++ b/Lib/http/server.py Wed Feb 24 11:00:00 2016 +0200 @@ -1174,16 +1174,14 @@ server_address = (bind, port) HandlerClass.protocol_version = protocol - httpd = ServerClass(server_address, HandlerClass) - - sa = httpd.socket.getsockname() - print("Serving HTTP on", sa[0], "port", sa[1], "...") - try: - httpd.serve_forever() - except KeyboardInterrupt: - print("\nKeyboard interrupt received, exiting.") - httpd.server_close() - sys.exit(0) + with ServerClass(server_address, HandlerClass) as httpd: + sa = httpd.socket.getsockname() + print("Serving HTTP on", sa[0], "port", sa[1], "...") + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\nKeyboard interrupt received, exiting.") + sys.exit(0) if __name__ == '__main__': parser = argparse.ArgumentParser() diff -r 5ffebeb3e91d Lib/socketserver.py --- a/Lib/socketserver.py Tue Feb 23 22:10:06 2016 +1100 +++ b/Lib/socketserver.py Wed Feb 24 11:00:00 2016 +0200 @@ -383,6 +383,12 @@ traceback.print_exc() print('-'*40, file=sys.stderr) + def __enter__(self): + return self + + def __exit__(self, *args): + self.server_close() + class TCPServer(BaseServer): diff -r 5ffebeb3e91d Lib/test/test_socketserver.py --- a/Lib/test/test_socketserver.py Tue Feb 23 22:10:06 2016 +1100 +++ b/Lib/test/test_socketserver.py Wed Feb 24 11:00:00 2016 +0200 @@ -104,7 +104,6 @@ class MyServer(svrcls): def handle_error(self, request, client_address): self.close_request(request) - self.server_close() raise class MyHandler(hdlrbase): @@ -119,32 +118,31 @@ @reap_threads def run_server(self, svrcls, hdlrbase, testfunc): - server = self.make_server(self.pickaddr(svrcls.address_family), - svrcls, hdlrbase) - # We had the OS pick a port, so pull the real address out of - # the server. - addr = server.server_address - if verbose: - print("ADDR =", addr) - print("CLASS =", svrcls) + with self.make_server(self.pickaddr(svrcls.address_family), + svrcls, hdlrbase) as server: + # We had the OS pick a port, so pull the real address out of + # the server. + addr = server.server_address + if verbose: + print("ADDR =", addr) + print("CLASS =", svrcls) - t = threading.Thread( - name='%s serving' % svrcls, - target=server.serve_forever, - # Short poll interval to make the test finish quickly. - # Time between requests is short enough that we won't wake - # up spuriously too many times. - kwargs={'poll_interval':0.01}) - t.daemon = True # In case this function raises. - t.start() - if verbose: print("server running") - for i in range(3): - if verbose: print("test client", i) - testfunc(svrcls.address_family, addr) - if verbose: print("waiting for server") - server.shutdown() - t.join() - server.server_close() + t = threading.Thread( + name='%s serving' % svrcls, + target=server.serve_forever, + # Short poll interval to make the test finish quickly. + # Time between requests is short enough that we won't wake + # up spuriously too many times. + kwargs={'poll_interval':0.01}) + t.daemon = True # In case this function raises. + t.start() + if verbose: print("server running") + for i in range(3): + if verbose: print("test client", i) + testfunc(svrcls.address_family, addr) + if verbose: print("waiting for server") + server.shutdown() + t.join() self.assertEqual(-1, server.socket.fileno()) if verbose: print("done") @@ -281,6 +279,12 @@ socketserver.TCPServer((HOST, -1), socketserver.StreamRequestHandler) + def test_server_close(self): + server = socketserver.TCPServer((HOST, 0), + socketserver.StreamRequestHandler) + server.server_close() + self.assertEqual(-1, server.socket.fileno()) + class ErrorHandlerTest(unittest.TestCase): """Test that the servers pass normal exceptions from the handler to diff -r 5ffebeb3e91d Lib/wsgiref/simple_server.py --- a/Lib/wsgiref/simple_server.py Tue Feb 23 22:10:06 2016 +1100 +++ b/Lib/wsgiref/simple_server.py Wed Feb 24 11:00:00 2016 +0200 @@ -156,10 +156,9 @@ if __name__ == '__main__': - httpd = make_server('', 8000, demo_app) - sa = httpd.socket.getsockname() - print("Serving HTTP on", sa[0], "port", sa[1], "...") - import webbrowser - webbrowser.open('http://localhost:8000/xyz?abc') - httpd.handle_request() # serve one request, then exit - httpd.server_close() + with make_server('', 8000, demo_app) as httpd: + sa = httpd.socket.getsockname() + print("Serving HTTP on", sa[0], "port", sa[1], "...") + import webbrowser + webbrowser.open('http://localhost:8000/xyz?abc') + httpd.handle_request() # serve one request, then exit diff -r 5ffebeb3e91d Lib/xmlrpc/server.py --- a/Lib/xmlrpc/server.py Tue Feb 23 22:10:06 2016 +1100 +++ b/Lib/xmlrpc/server.py Wed Feb 24 11:00:00 2016 +0200 @@ -971,16 +971,15 @@ def getCurrentTime(): return datetime.datetime.now() - server = SimpleXMLRPCServer(("localhost", 8000)) - server.register_function(pow) - server.register_function(lambda x,y: x+y, 'add') - server.register_instance(ExampleService(), allow_dotted_names=True) - server.register_multicall_functions() - print('Serving XML-RPC on localhost port 8000') - print('It is advisable to run this example server within a secure, closed network.') - try: - server.serve_forever() - except KeyboardInterrupt: - print("\nKeyboard interrupt received, exiting.") - server.server_close() - sys.exit(0) + with SimpleXMLRPCServer(("localhost", 8000)) as server: + server.register_function(pow) + server.register_function(lambda x,y: x+y, 'add') + server.register_instance(ExampleService(), allow_dotted_names=True) + server.register_multicall_functions() + print('Serving XML-RPC on localhost port 8000') + print('It is advisable to run this example server within a secure, closed network.') + try: + server.serve_forever() + except KeyboardInterrupt: + print("\nKeyboard interrupt received, exiting.") + sys.exit(0)