Make DaphneProcess pickleable (#440)

This commit is contained in:
Adam Johnson 2022-10-07 12:22:40 +01:00 committed by GitHub
parent fef1490eff
commit 12e543750b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 19 additions and 9 deletions

View File

@ -41,6 +41,9 @@ This is a beta release to allow testing compatibility with the upcoming Channels
* Removed deprecated ``--ws_protocols`` CLI option. * Removed deprecated ``--ws_protocols`` CLI option.
* Made the ``DaphneProcess`` tests helper class compatible with the ``spawn``
process start method, which is used on macOS and Windows.
3.0.2 (2021-04-07) 3.0.2 (2021-04-07)
------------------ ------------------

View File

@ -26,6 +26,9 @@ class BaseDaphneTestingInstance:
self.request_buffer_size = request_buffer_size self.request_buffer_size = request_buffer_size
self.application = application self.application = application
def get_application(self):
return self.application
def __enter__(self): def __enter__(self):
# Option Daphne features # Option Daphne features
kwargs = {} kwargs = {}
@ -41,7 +44,7 @@ class BaseDaphneTestingInstance:
# Start up process # Start up process
self.process = DaphneProcess( self.process = DaphneProcess(
host=self.host, host=self.host,
application=self.application, get_application=self.get_application,
kwargs=kwargs, kwargs=kwargs,
setup=self.process_setup, setup=self.process_setup,
teardown=self.process_teardown, teardown=self.process_teardown,
@ -123,13 +126,13 @@ class DaphneProcess(multiprocessing.Process):
port it ends up listening on back to the parent process. port it ends up listening on back to the parent process.
""" """
def __init__(self, host, application, kwargs=None, setup=None, teardown=None): def __init__(self, host, get_application, kwargs=None, setup=None, teardown=None):
super().__init__() super().__init__()
self.host = host self.host = host
self.application = application self.get_application = get_application
self.kwargs = kwargs or {} self.kwargs = kwargs or {}
self.setup = setup or (lambda: None) self.setup = setup
self.teardown = teardown or (lambda: None) self.teardown = teardown
self.port = multiprocessing.Value("i") self.port = multiprocessing.Value("i")
self.ready = multiprocessing.Event() self.ready = multiprocessing.Event()
self.errors = multiprocessing.Queue() self.errors = multiprocessing.Queue()
@ -146,11 +149,13 @@ class DaphneProcess(multiprocessing.Process):
from .endpoints import build_endpoint_description_strings from .endpoints import build_endpoint_description_strings
from .server import Server from .server import Server
application = self.get_application()
try: try:
# Create the server class # Create the server class
endpoints = build_endpoint_description_strings(host=self.host, port=0) endpoints = build_endpoint_description_strings(host=self.host, port=0)
self.server = Server( self.server = Server(
application=self.application, application=application,
endpoints=endpoints, endpoints=endpoints,
signal_handlers=False, signal_handlers=False,
**self.kwargs **self.kwargs
@ -158,11 +163,13 @@ class DaphneProcess(multiprocessing.Process):
# Set up a poller to look for the port # Set up a poller to look for the port
reactor.callLater(0.1, self.resolve_port) reactor.callLater(0.1, self.resolve_port)
# Run with setup/teardown # Run with setup/teardown
self.setup() if self.setup is not None:
self.setup()
try: try:
self.server.run() self.server.run()
finally: finally:
self.teardown() if self.teardown is not None:
self.teardown()
except BaseException as e: except BaseException as e:
# Put the error on our queue so the parent gets it # Put the error on our queue so the parent gets it
self.errors.put((e, traceback.format_exc())) self.errors.put((e, traceback.format_exc()))

View File

@ -288,7 +288,7 @@ async def cancelling_application(scope, receive, send):
from twisted.internet import reactor from twisted.internet import reactor
# Stop the server after a short delay so that the teardown is run. # Stop the server after a short delay so that the teardown is run.
reactor.callLater(2, lambda: reactor.stop()) reactor.callLater(2, reactor.stop)
await send({"type": "websocket.accept"}) await send({"type": "websocket.accept"})
raise asyncio.CancelledError() raise asyncio.CancelledError()