From 7032f8e0f82afdc3bd606b08f1443c1245e2774d Mon Sep 17 00:00:00 2001 From: Joonhyung Shin Date: Thu, 7 Nov 2019 03:51:00 +0900 Subject: [PATCH] Resolve asyncio + multiprocessing problem when testing. (#247) --- .gitignore | 1 + .travis.yml | 37 +++++++++++++++++++++++++++++++++++++ daphne/testing.py | 39 ++++++++++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 830060b..b3cff06 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea/ *.egg-info *.pyc __pycache__ diff --git a/.travis.yml b/.travis.yml index d87df46..02274ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,43 @@ jobs: dist: xenial sudo: required + - os: osx + language: generic + python: '3.5' + env: TWISTED="twisted==18.7.0" + before_install: + - eval "$(pyenv init -)" + - pyenv install 3.5.5 + - pyenv global 3.5.5 + sudo: required + - os: osx + language: generic + python: '3.5' + env: TWISTED="twisted" + before_install: + - eval "$(pyenv init -)" + - pyenv install 3.5.5 + - pyenv global 3.5.5 + sudo: required + - os: osx + language: generic + python: '3.6' + env: TWISTED="twisted==18.7.0" + before_install: + - eval "$(pyenv init -)" + - pyenv install 3.6.5 + - pyenv global 3.6.5 + sudo: required + - os: osx + language: generic + python: '3.6' + env: TWISTED="twisted" + before_install: + - eval "$(pyenv init -)" + - pyenv install 3.6.5 + - pyenv global 3.6.5 + sudo: required + - stage: lint install: pip install -U -e .[tests] black pyflakes isort script: diff --git a/daphne/testing.py b/daphne/testing.py index f5f3724..0da03d0 100644 --- a/daphne/testing.py +++ b/daphne/testing.py @@ -6,11 +6,6 @@ import tempfile import traceback from concurrent.futures import CancelledError -from twisted.internet import reactor - -from .endpoints import build_endpoint_description_strings -from .server import Server - class DaphneTestingInstance: """ @@ -121,6 +116,17 @@ class DaphneProcess(multiprocessing.Process): self.errors = multiprocessing.Queue() def run(self): + # OK, now we are in a forked child process, and want to use the reactor. + # However, FreeBSD systems like MacOS do not fork the underlying Kqueue, + # which asyncio (hence asyncioreactor) is built on. + # Therefore, we should uninstall the broken reactor and install a new one. + _reinstall_reactor() + + from twisted.internet import reactor + + from .server import Server + from .endpoints import build_endpoint_description_strings + try: # Create the server class endpoints = build_endpoint_description_strings(host=self.host, port=0) @@ -143,6 +149,8 @@ class DaphneProcess(multiprocessing.Process): self.errors.put((e, traceback.format_exc())) def resolve_port(self): + from twisted.internet import reactor + if self.server.listening_addresses: self.port.value = self.server.listening_addresses[0][1] self.ready.set() @@ -249,3 +257,24 @@ class TestApplication: os.unlink(cls.result_storage) except OSError: pass + + +def _reinstall_reactor(): + import sys + import asyncio + + from twisted.internet import asyncioreactor + + # Uninstall the reactor. + if "twisted.internet.reactor" in sys.modules: + del sys.modules["twisted.internet.reactor"] + + # The daphne.server module may have already installed the reactor. + # If so, using this module will use uninstalled one, thus we should + # reimport this module too. + if "daphne.server" in sys.modules: + del sys.modules["daphne.server"] + + event_loop = asyncio.new_event_loop() + asyncioreactor.install(event_loop) + asyncio.set_event_loop(event_loop)