mirror of
https://github.com/python/cpython.git
synced 2024-11-23 18:04:37 +08:00
Issue #26404: Add context manager to socketserver, by Aviv Palivoda
This commit is contained in:
parent
7258176c68
commit
0cab9c1eba
@ -375,10 +375,9 @@ the current directory::
|
|||||||
|
|
||||||
Handler = http.server.SimpleHTTPRequestHandler
|
Handler = http.server.SimpleHTTPRequestHandler
|
||||||
|
|
||||||
httpd = socketserver.TCPServer(("", PORT), Handler)
|
with socketserver.TCPServer(("", PORT), Handler) as httpd:
|
||||||
|
print("serving at port", PORT)
|
||||||
print("serving at port", PORT)
|
httpd.serve_forever()
|
||||||
httpd.serve_forever()
|
|
||||||
|
|
||||||
.. _http-server-cli:
|
.. _http-server-cli:
|
||||||
|
|
||||||
|
@ -52,11 +52,12 @@ handler class by subclassing the :class:`BaseRequestHandler` class and
|
|||||||
overriding its :meth:`~BaseRequestHandler.handle` method;
|
overriding its :meth:`~BaseRequestHandler.handle` method;
|
||||||
this method will process incoming
|
this method will process incoming
|
||||||
requests. Second, you must instantiate one of the server classes, passing it
|
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.handle_request` or
|
||||||
:meth:`~BaseServer.serve_forever` method of the server object to
|
:meth:`~BaseServer.serve_forever` method of the server object to
|
||||||
process one or many requests. Finally, call :meth:`~BaseServer.server_close`
|
process one or many requests. Finally, call :meth:`~BaseServer.server_close`
|
||||||
to close the socket.
|
to close the socket (unless you used a :keyword:`with` statement).
|
||||||
|
|
||||||
When inheriting from :class:`ThreadingMixIn` for threaded connection behavior,
|
When inheriting from :class:`ThreadingMixIn` for threaded connection behavior,
|
||||||
you should explicitly declare how you want your threads to behave on an abrupt
|
you should explicitly declare how you want your threads to behave on an abrupt
|
||||||
@ -353,6 +354,11 @@ Server Objects
|
|||||||
default implementation always returns :const:`True`.
|
default implementation always returns :const:`True`.
|
||||||
|
|
||||||
|
|
||||||
|
.. versionchanged:: 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
|
Request Handler Objects
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
@ -433,11 +439,10 @@ This is the server side::
|
|||||||
HOST, PORT = "localhost", 9999
|
HOST, PORT = "localhost", 9999
|
||||||
|
|
||||||
# Create the server, binding to localhost on port 9999
|
# Create the server, binding to localhost on port 9999
|
||||||
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
|
with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
|
||||||
|
# Activate the server; this will keep running until you
|
||||||
# Activate the server; this will keep running until you
|
# interrupt the program with Ctrl-C
|
||||||
# interrupt the program with Ctrl-C
|
server.serve_forever()
|
||||||
server.serve_forever()
|
|
||||||
|
|
||||||
An alternative request handler class that makes use of streams (file-like
|
An alternative request handler class that makes use of streams (file-like
|
||||||
objects that simplify communication by providing the standard file interface)::
|
objects that simplify communication by providing the standard file interface)::
|
||||||
@ -529,8 +534,8 @@ This is the server side::
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
HOST, PORT = "localhost", 9999
|
HOST, PORT = "localhost", 9999
|
||||||
server = socketserver.UDPServer((HOST, PORT), MyUDPHandler)
|
with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
|
|
||||||
This is the client side::
|
This is the client side::
|
||||||
|
|
||||||
@ -592,22 +597,22 @@ An example for the :class:`ThreadingMixIn` class::
|
|||||||
HOST, PORT = "localhost", 0
|
HOST, PORT = "localhost", 0
|
||||||
|
|
||||||
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
|
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
|
||||||
ip, port = server.server_address
|
with server:
|
||||||
|
ip, port = server.server_address
|
||||||
|
|
||||||
# Start a thread with the server -- that thread will then start one
|
# Start a thread with the server -- that thread will then start one
|
||||||
# more thread for each request
|
# more thread for each request
|
||||||
server_thread = threading.Thread(target=server.serve_forever)
|
server_thread = threading.Thread(target=server.serve_forever)
|
||||||
# Exit the server thread when the main thread terminates
|
# Exit the server thread when the main thread terminates
|
||||||
server_thread.daemon = True
|
server_thread.daemon = True
|
||||||
server_thread.start()
|
server_thread.start()
|
||||||
print("Server loop running in thread:", server_thread.name)
|
print("Server loop running in thread:", server_thread.name)
|
||||||
|
|
||||||
client(ip, port, "Hello World 1")
|
client(ip, port, "Hello World 1")
|
||||||
client(ip, port, "Hello World 2")
|
client(ip, port, "Hello World 2")
|
||||||
client(ip, port, "Hello World 3")
|
client(ip, port, "Hello World 3")
|
||||||
|
|
||||||
server.shutdown()
|
server.shutdown()
|
||||||
server.server_close()
|
|
||||||
|
|
||||||
|
|
||||||
The output of the example should look something like this::
|
The output of the example should look something like this::
|
||||||
|
@ -131,9 +131,9 @@ parameter expect a WSGI-compliant dictionary to be supplied; please see
|
|||||||
for key, value in environ.items()]
|
for key, value in environ.items()]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
httpd = make_server('', 8000, simple_app)
|
with make_server('', 8000, simple_app) as httpd:
|
||||||
print("Serving on port 8000...")
|
print("Serving on port 8000...")
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
In addition to the environment functions above, the :mod:`wsgiref.util` module
|
In addition to the environment functions above, the :mod:`wsgiref.util` module
|
||||||
@ -283,14 +283,14 @@ request. (E.g., using the :func:`shift_path_info` function from
|
|||||||
|
|
||||||
from wsgiref.simple_server import make_server, demo_app
|
from wsgiref.simple_server import make_server, demo_app
|
||||||
|
|
||||||
httpd = make_server('', 8000, demo_app)
|
with make_server('', 8000, demo_app) as httpd:
|
||||||
print("Serving HTTP on port 8000...")
|
print("Serving HTTP on port 8000...")
|
||||||
|
|
||||||
# Respond to requests until process is killed
|
# Respond to requests until process is killed
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
|
|
||||||
# Alternative: serve one request, then exit
|
# Alternative: serve one request, then exit
|
||||||
httpd.handle_request()
|
httpd.handle_request()
|
||||||
|
|
||||||
|
|
||||||
.. function:: demo_app(environ, start_response)
|
.. function:: demo_app(environ, start_response)
|
||||||
@ -430,9 +430,9 @@ Paste" library.
|
|||||||
# This is the application wrapped in a validator
|
# This is the application wrapped in a validator
|
||||||
validator_app = validator(simple_app)
|
validator_app = validator(simple_app)
|
||||||
|
|
||||||
httpd = make_server('', 8000, validator_app)
|
with make_server('', 8000, validator_app) as httpd:
|
||||||
print("Listening on port 8000....")
|
print("Listening on port 8000....")
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
:mod:`wsgiref.handlers` -- server/gateway base classes
|
:mod:`wsgiref.handlers` -- server/gateway base classes
|
||||||
@ -769,8 +769,8 @@ This is a working "Hello World" WSGI application::
|
|||||||
# The returned object is going to be printed
|
# The returned object is going to be printed
|
||||||
return [b"Hello World"]
|
return [b"Hello World"]
|
||||||
|
|
||||||
httpd = make_server('', 8000, hello_world_app)
|
with make_server('', 8000, hello_world_app) as httpd:
|
||||||
print("Serving on port 8000...")
|
print("Serving on port 8000...")
|
||||||
|
|
||||||
# Serve until process is killed
|
# Serve until process is killed
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
|
@ -147,29 +147,29 @@ Server code::
|
|||||||
rpc_paths = ('/RPC2',)
|
rpc_paths = ('/RPC2',)
|
||||||
|
|
||||||
# Create server
|
# Create server
|
||||||
server = SimpleXMLRPCServer(("localhost", 8000),
|
with SimpleXMLRPCServer(("localhost", 8000),
|
||||||
requestHandler=RequestHandler)
|
requestHandler=RequestHandler) as server:
|
||||||
server.register_introspection_functions()
|
server.register_introspection_functions()
|
||||||
|
|
||||||
# Register pow() function; this will use the value of
|
# Register pow() function; this will use the value of
|
||||||
# pow.__name__ as the name, which is just 'pow'.
|
# pow.__name__ as the name, which is just 'pow'.
|
||||||
server.register_function(pow)
|
server.register_function(pow)
|
||||||
|
|
||||||
# Register a function under a different name
|
# Register a function under a different name
|
||||||
def adder_function(x,y):
|
def adder_function(x,y):
|
||||||
return x + y
|
return x + y
|
||||||
server.register_function(adder_function, 'add')
|
server.register_function(adder_function, 'add')
|
||||||
|
|
||||||
# Register an instance; all the methods of the instance are
|
# Register an instance; all the methods of the instance are
|
||||||
# published as XML-RPC methods (in this case, just 'mul').
|
# published as XML-RPC methods (in this case, just 'mul').
|
||||||
class MyFuncs:
|
class MyFuncs:
|
||||||
def mul(self, x, y):
|
def mul(self, x, y):
|
||||||
return x * y
|
return x * y
|
||||||
|
|
||||||
server.register_instance(MyFuncs())
|
server.register_instance(MyFuncs())
|
||||||
|
|
||||||
# Run the server's main loop
|
# Run the server's main loop
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
|
|
||||||
The following client code will call the methods made available by the preceding
|
The following client code will call the methods made available by the preceding
|
||||||
server::
|
server::
|
||||||
@ -206,18 +206,17 @@ a server allowing dotted names and registering a multicall function.
|
|||||||
def getCurrentTime():
|
def getCurrentTime():
|
||||||
return datetime.datetime.now()
|
return datetime.datetime.now()
|
||||||
|
|
||||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
with SimpleXMLRPCServer(("localhost", 8000)) as server:
|
||||||
server.register_function(pow)
|
server.register_function(pow)
|
||||||
server.register_function(lambda x,y: x+y, 'add')
|
server.register_function(lambda x,y: x+y, 'add')
|
||||||
server.register_instance(ExampleService(), allow_dotted_names=True)
|
server.register_instance(ExampleService(), allow_dotted_names=True)
|
||||||
server.register_multicall_functions()
|
server.register_multicall_functions()
|
||||||
print('Serving XML-RPC on localhost port 8000')
|
print('Serving XML-RPC on localhost port 8000')
|
||||||
try:
|
try:
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nKeyboard interrupt received, exiting.")
|
print("\nKeyboard interrupt received, exiting.")
|
||||||
server.server_close()
|
sys.exit(0)
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
This ExampleService demo can be invoked from the command line::
|
This ExampleService demo can be invoked from the command line::
|
||||||
|
|
||||||
|
@ -259,6 +259,16 @@ you may now specify file paths on top of directories (e.g. zip files).
|
|||||||
(Contributed by Wolfgang Langner in :issue:`26587`).
|
(Contributed by Wolfgang Langner in :issue:`26587`).
|
||||||
|
|
||||||
|
|
||||||
|
socketserver
|
||||||
|
------------
|
||||||
|
|
||||||
|
Servers based on the :mod:`socketserver` module, including those
|
||||||
|
defined in :mod:`http.server`, :mod:`xmlrpc.server` and
|
||||||
|
:mod:`wsgiref.simple_server`, now support the :term:`context manager`
|
||||||
|
protocol.
|
||||||
|
(Contributed by Aviv Palivoda in :issue:`26404`.)
|
||||||
|
|
||||||
|
|
||||||
telnetlib
|
telnetlib
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
@ -1175,16 +1175,14 @@ def test(HandlerClass=BaseHTTPRequestHandler,
|
|||||||
server_address = (bind, port)
|
server_address = (bind, port)
|
||||||
|
|
||||||
HandlerClass.protocol_version = protocol
|
HandlerClass.protocol_version = protocol
|
||||||
httpd = ServerClass(server_address, HandlerClass)
|
with ServerClass(server_address, HandlerClass) as httpd:
|
||||||
|
sa = httpd.socket.getsockname()
|
||||||
sa = httpd.socket.getsockname()
|
print("Serving HTTP on", sa[0], "port", sa[1], "...")
|
||||||
print("Serving HTTP on", sa[0], "port", sa[1], "...")
|
try:
|
||||||
try:
|
httpd.serve_forever()
|
||||||
httpd.serve_forever()
|
except KeyboardInterrupt:
|
||||||
except KeyboardInterrupt:
|
print("\nKeyboard interrupt received, exiting.")
|
||||||
print("\nKeyboard interrupt received, exiting.")
|
sys.exit(0)
|
||||||
httpd.server_close()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
@ -378,6 +378,12 @@ class BaseServer:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
print('-'*40, file=sys.stderr)
|
print('-'*40, file=sys.stderr)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
self.server_close()
|
||||||
|
|
||||||
|
|
||||||
class TCPServer(BaseServer):
|
class TCPServer(BaseServer):
|
||||||
|
|
||||||
|
@ -104,7 +104,6 @@ class SocketServerTest(unittest.TestCase):
|
|||||||
class MyServer(svrcls):
|
class MyServer(svrcls):
|
||||||
def handle_error(self, request, client_address):
|
def handle_error(self, request, client_address):
|
||||||
self.close_request(request)
|
self.close_request(request)
|
||||||
self.server_close()
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
class MyHandler(hdlrbase):
|
class MyHandler(hdlrbase):
|
||||||
@ -280,6 +279,12 @@ class SocketServerTest(unittest.TestCase):
|
|||||||
socketserver.TCPServer((HOST, -1),
|
socketserver.TCPServer((HOST, -1),
|
||||||
socketserver.StreamRequestHandler)
|
socketserver.StreamRequestHandler)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
with socketserver.TCPServer((HOST, 0),
|
||||||
|
socketserver.StreamRequestHandler) as server:
|
||||||
|
pass
|
||||||
|
self.assertEqual(-1, server.socket.fileno())
|
||||||
|
|
||||||
|
|
||||||
class ErrorHandlerTest(unittest.TestCase):
|
class ErrorHandlerTest(unittest.TestCase):
|
||||||
"""Test that the servers pass normal exceptions from the handler to
|
"""Test that the servers pass normal exceptions from the handler to
|
||||||
|
@ -156,10 +156,9 @@ def make_server(
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
httpd = make_server('', 8000, demo_app)
|
with make_server('', 8000, demo_app) as httpd:
|
||||||
sa = httpd.socket.getsockname()
|
sa = httpd.socket.getsockname()
|
||||||
print("Serving HTTP on", sa[0], "port", sa[1], "...")
|
print("Serving HTTP on", sa[0], "port", sa[1], "...")
|
||||||
import webbrowser
|
import webbrowser
|
||||||
webbrowser.open('http://localhost:8000/xyz?abc')
|
webbrowser.open('http://localhost:8000/xyz?abc')
|
||||||
httpd.handle_request() # serve one request, then exit
|
httpd.handle_request() # serve one request, then exit
|
||||||
httpd.server_close()
|
|
||||||
|
@ -971,16 +971,15 @@ if __name__ == '__main__':
|
|||||||
def getCurrentTime():
|
def getCurrentTime():
|
||||||
return datetime.datetime.now()
|
return datetime.datetime.now()
|
||||||
|
|
||||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
with SimpleXMLRPCServer(("localhost", 8000)) as server:
|
||||||
server.register_function(pow)
|
server.register_function(pow)
|
||||||
server.register_function(lambda x,y: x+y, 'add')
|
server.register_function(lambda x,y: x+y, 'add')
|
||||||
server.register_instance(ExampleService(), allow_dotted_names=True)
|
server.register_instance(ExampleService(), allow_dotted_names=True)
|
||||||
server.register_multicall_functions()
|
server.register_multicall_functions()
|
||||||
print('Serving XML-RPC on localhost port 8000')
|
print('Serving XML-RPC on localhost port 8000')
|
||||||
print('It is advisable to run this example server within a secure, closed network.')
|
print('It is advisable to run this example server within a secure, closed network.')
|
||||||
try:
|
try:
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nKeyboard interrupt received, exiting.")
|
print("\nKeyboard interrupt received, exiting.")
|
||||||
server.server_close()
|
sys.exit(0)
|
||||||
sys.exit(0)
|
|
||||||
|
@ -240,6 +240,8 @@ Core and Builtins
|
|||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #26404: Add context manager to socketserver. Patch by Aviv Palivoda.
|
||||||
|
|
||||||
- Issue #26735: Fix :func:`os.urandom` on Solaris 11.3 and newer when reading
|
- Issue #26735: Fix :func:`os.urandom` on Solaris 11.3 and newer when reading
|
||||||
more than 1,024 bytes: call ``getrandom()`` multiple times with a limit of
|
more than 1,024 bytes: call ``getrandom()`` multiple times with a limit of
|
||||||
1024 bytes per call.
|
1024 bytes per call.
|
||||||
|
Loading…
Reference in New Issue
Block a user