From c5b669da0f075cad5f9dcfb2889fd95dc222c35c Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 15 Jun 2022 10:41:30 +0200 Subject: [PATCH] Allow passing asyncio_loop argument to AsyncIOLoop allows patterns of creating and explicitly passing the asyncio loop before creating IOLoop For example: creating a loop with the non-default event loop policy without having to set the current policy --- tornado/platform/asyncio.py | 15 ++++++++++++--- tornado/test/ioloop_test.py | 7 +++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/tornado/platform/asyncio.py b/tornado/platform/asyncio.py index 58d31ddd81..17c92dbbfb 100644 --- a/tornado/platform/asyncio.py +++ b/tornado/platform/asyncio.py @@ -310,6 +310,12 @@ class AsyncIOLoop(BaseAsyncIOLoop): Each ``AsyncIOLoop`` creates a new ``asyncio.EventLoop``; this object can be accessed with the ``asyncio_loop`` attribute. + .. versionchanged:: 6.2 + + Support explicit `asyncio_loop` argument + for specifying the asyncio loop to attach to, + rather than always creating a new one with the default policy. + .. versionchanged:: 5.0 When an ``AsyncIOLoop`` becomes the current `.IOLoop`, it also sets @@ -323,13 +329,16 @@ class AsyncIOLoop(BaseAsyncIOLoop): def initialize(self, **kwargs: Any) -> None: # type: ignore self.is_current = False - loop = asyncio.new_event_loop() + loop = None + if "asyncio_loop" not in kwargs: + kwargs["asyncio_loop"] = loop = asyncio.new_event_loop() try: - super().initialize(loop, **kwargs) + super().initialize(**kwargs) except Exception: # If initialize() does not succeed (taking ownership of the loop), # we have to close it. - loop.close() + if loop is not None: + loop.close() raise def close(self, all_fds: bool = False) -> None: diff --git a/tornado/test/ioloop_test.py b/tornado/test/ioloop_test.py index 825f5a2a53..65e5bcbc6c 100644 --- a/tornado/test/ioloop_test.py +++ b/tornado/test/ioloop_test.py @@ -1,3 +1,4 @@ +import asyncio from concurrent.futures import ThreadPoolExecutor from concurrent import futures from collections.abc import Generator @@ -427,6 +428,12 @@ def f(): yield gen.multi([self.io_loop.run_in_executor(None, f) for i in range(2)]) + def test_explicit_asyncio_loop(self): + asyncio_loop = asyncio.new_event_loop() + loop = IOLoop(asyncio_loop=asyncio_loop, make_current=False) + assert loop.asyncio_loop is asyncio_loop + loop.close() + # Deliberately not a subclass of AsyncTestCase so the IOLoop isn't # automatically set as current.