diff --git a/jupyter_server/pytest_plugin.py b/jupyter_server/pytest_plugin.py index 849cdc469b..f187687535 100644 --- a/jupyter_server/pytest_plugin.py +++ b/jupyter_server/pytest_plugin.py @@ -31,6 +31,12 @@ # "jupyter_core.pytest_plugin" ] + +import asyncio +if os.name == "nt" and sys.version_info >= (3, 7): + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + # ============ Move to Jupyter Core ============= def mkdir(tmp_path, *parts): diff --git a/jupyter_server/serverapp.py b/jupyter_server/serverapp.py index eb3b17cd92..e3e404e541 100755 --- a/jupyter_server/serverapp.py +++ b/jupyter_server/serverapp.py @@ -1810,12 +1810,34 @@ def init_httpserver(self): @staticmethod def _init_asyncio_patch(): - """no longer needed with tornado 6.1""" - warnings.warn( - """ServerApp._init_asyncio_patch called, and is longer needed for """ - """tornado 6.1+, and will be removed in a future release.""", - DeprecationWarning - ) + """set default asyncio policy to be compatible with tornado + + Tornado 6.0 is not compatible with default asyncio + ProactorEventLoop, which lacks basic *_reader methods. + Tornado 6.1 adds a workaround to add these methods in a thread, + but SelectorEventLoop should still be preferred + to avoid the extra thread for ~all of our events, + at least until asyncio adds *_reader methods + to proactor. + """ + if sys.platform.startswith("win") and sys.version_info >= (3, 8): + import asyncio + + try: + from asyncio import ( + WindowsProactorEventLoopPolicy, + WindowsSelectorEventLoopPolicy, + ) + except ImportError: + pass + # not affected + else: + if ( + type(asyncio.get_event_loop_policy()) + is WindowsProactorEventLoopPolicy + ): + # prefer Selector to Proactor for tornado + pyzmq + asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy()) @catch_config_error def initialize(self, argv=None, find_extensions=True, new_httpserver=True, starter_extension=None): @@ -1836,6 +1858,7 @@ def initialize(self, argv=None, find_extensions=True, new_httpserver=True, start If given, it references the name of an extension point that started the Server. We will try to load configuration from extension point """ + self._init_asyncio_patch() # Parse command line, load ServerApp config files, # and update ServerApp config. super(ServerApp, self).initialize(argv=argv)