From 64dcf5bf1cbc41d07e4aeb60ffe60ec94a9832db Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 2 Jun 2022 08:10:57 +0300 Subject: [PATCH 1/2] gh-93453: Make get_event_loop() an alias of get_running_loop() --- Doc/library/asyncio-eventloop.rst | 31 +++---- Doc/library/asyncio-llapi-index.rst | 6 +- Doc/library/asyncio-policy.rst | 2 +- Lib/asyncio/events.py | 30 +------ Lib/asyncio/futures.py | 7 +- Lib/asyncio/streams.py | 10 +-- Lib/asyncio/tasks.py | 6 +- Lib/test/test_asyncio/test_base_events.py | 2 +- Lib/test/test_asyncio/test_events.py | 51 ++++-------- Lib/test/test_asyncio/test_futures.py | 29 +++---- Lib/test/test_asyncio/test_streams.py | 28 +++---- Lib/test/test_asyncio/test_tasks.py | 80 +++++++------------ Lib/test/test_coroutines.py | 3 +- ...2-06-03-10-32-54.gh-issue-93453.M3583s.rst | 2 + Modules/_asynciomodule.c | 73 ++--------------- Modules/clinic/_asynciomodule.c.h | 66 +-------------- 16 files changed, 105 insertions(+), 321 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-06-03-10-32-54.gh-issue-93453.M3583s.rst diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 4f0f8c06fee787..bd12ae60395a9d 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -38,25 +38,12 @@ an event loop: .. function:: get_event_loop() - Get the current event loop. + An alias of :func:`get_running_loop`. - If there is no current event loop set in the current OS thread, - the OS thread is main, and :func:`set_event_loop` has not yet - been called, asyncio will create a new event loop and set it as the - current one. - - Because this function has rather complex behavior (especially - when custom event loop policies are in use), using the - :func:`get_running_loop` function is preferred to :func:`get_event_loop` - in coroutines and callbacks. - - Consider also using the :func:`asyncio.run` function instead of using - lower level functions to manually create and close an event loop. - - .. deprecated:: 3.10 - Deprecation warning is emitted if there is no running event loop. - In future Python releases, this function will be an alias of - :func:`get_running_loop`. + .. versionchanged:: 3.12 + Prior to Python 3.12, this function created a new event loop if there + was no running event loop and set it as a current event loop for the + current OS thread. .. function:: set_event_loop(loop) @@ -66,7 +53,7 @@ an event loop: Create and return a new event loop object. -Note that the behaviour of :func:`get_event_loop`, :func:`set_event_loop`, +Note that the behaviour of :func:`set_event_loop` and :func:`new_event_loop` functions can be altered by :ref:`setting a custom event loop policy `. @@ -1663,7 +1650,7 @@ event loop:: print('Hello World') loop.stop() - loop = asyncio.get_event_loop() + loop = asyncio.new_event_loop() # Schedule a call to hello_world() loop.call_soon(hello_world, loop) @@ -1699,7 +1686,7 @@ after 5 seconds, and then stops the event loop:: else: loop.stop() - loop = asyncio.get_event_loop() + loop = asyncio.new_event_loop() # Schedule the first call to display_date() end_time = loop.time() + 5.0 @@ -1731,7 +1718,7 @@ Wait until a file descriptor received some data using the # Create a pair of connected file descriptors rsock, wsock = socketpair() - loop = asyncio.get_event_loop() + loop = asyncio.new_event_loop() def reader(): data = rsock.recv(100) diff --git a/Doc/library/asyncio-llapi-index.rst b/Doc/library/asyncio-llapi-index.rst index 69b550e43f5aa9..3c245edb16d025 100644 --- a/Doc/library/asyncio-llapi-index.rst +++ b/Doc/library/asyncio-llapi-index.rst @@ -19,7 +19,7 @@ Obtaining the Event Loop - The **preferred** function to get the running event loop. * - :func:`asyncio.get_event_loop` - - Get an event loop instance (current or via the policy). + - An alias of :func:`asyncio.get_running_loop`. * - :func:`asyncio.set_event_loop` - Set the event loop as current via the current policy. @@ -267,7 +267,7 @@ See also the main documentation section about the .. rubric:: Examples -* :ref:`Using asyncio.get_event_loop() and loop.run_forever() +* :ref:`Using asyncio.get_running_loop() and loop.run_forever() `. * :ref:`Using loop.call_later() `. @@ -499,7 +499,7 @@ Event Loop Policies =================== Policies is a low-level mechanism to alter the behavior of -functions like :func:`asyncio.get_event_loop`. See also +functions like :func:`asyncio.new_event_loop`. See also the main :ref:`policies section ` for more details. diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst index ef6a0588506b52..bf11598a77df9f 100644 --- a/Doc/library/asyncio-policy.rst +++ b/Doc/library/asyncio-policy.rst @@ -16,7 +16,7 @@ separate event loop per context. The default policy defines *context* to be the current thread. By using a custom event loop policy, the behavior of -:func:`get_event_loop`, :func:`set_event_loop`, and +:func:`set_event_loop` and :func:`new_event_loop` functions can be customized. Policy objects should implement the APIs defined diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index 0d26ea545baa5d..002655bf905651 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -772,27 +772,7 @@ def set_event_loop_policy(policy): _event_loop_policy = policy -def get_event_loop(): - """Return an asyncio event loop. - - When called from a coroutine or a callback (e.g. scheduled with call_soon - or similar API), this function will always return the running event loop. - - If there is no running event loop set, the function will return - the result of `get_event_loop_policy().get_event_loop()` call. - """ - # NOTE: this function is implemented in C (see _asynciomodule.c) - return _py__get_event_loop() - - -def _get_event_loop(stacklevel=3): - current_loop = _get_running_loop() - if current_loop is not None: - return current_loop - import warnings - warnings.warn('There is no current event loop', - DeprecationWarning, stacklevel=stacklevel) - return get_event_loop_policy().get_event_loop() +get_event_loop = get_running_loop def set_event_loop(loop): @@ -820,8 +800,6 @@ def set_child_watcher(watcher): _py__get_running_loop = _get_running_loop _py__set_running_loop = _set_running_loop _py_get_running_loop = get_running_loop -_py_get_event_loop = get_event_loop -_py__get_event_loop = _get_event_loop try: @@ -829,7 +807,7 @@ def set_child_watcher(watcher): # functions in asyncio. Pure Python implementation is # about 4 times slower than C-accelerated. from _asyncio import (_get_running_loop, _set_running_loop, - get_running_loop, get_event_loop, _get_event_loop) + get_running_loop) except ImportError: pass else: @@ -837,5 +815,5 @@ def set_child_watcher(watcher): _c__get_running_loop = _get_running_loop _c__set_running_loop = _set_running_loop _c_get_running_loop = get_running_loop - _c_get_event_loop = get_event_loop - _c__get_event_loop = _get_event_loop + +get_event_loop = get_running_loop diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 08c79e7b5109bf..f379035de2c78f 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -78,9 +78,8 @@ def __init__(self, *, loop=None): the default event loop. """ if loop is None: - self._loop = events._get_event_loop() - else: - self._loop = loop + loop = events.get_running_loop() + self._loop = loop self._callbacks = [] if self._loop.get_debug(): self._source_traceback = format_helpers.extract_stack( @@ -416,7 +415,7 @@ def wrap_future(future, *, loop=None): assert isinstance(future, concurrent.futures.Future), \ f'concurrent.futures.Future is expected, got {future!r}' if loop is None: - loop = events._get_event_loop() + loop = events.get_running_loop() new_future = loop.create_future() _chain_future(future, new_future) return new_future diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index a568c4e4b295f0..17a80a3c9e5e9f 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -125,9 +125,8 @@ class FlowControlMixin(protocols.Protocol): def __init__(self, loop=None): if loop is None: - self._loop = events._get_event_loop(stacklevel=4) - else: - self._loop = loop + loop = events.get_running_loop() + self._loop = loop self._paused = False self._drain_waiter = None self._connection_lost = False @@ -406,9 +405,8 @@ def __init__(self, limit=_DEFAULT_LIMIT, loop=None): self._limit = limit if loop is None: - self._loop = events._get_event_loop() - else: - self._loop = loop + loop = events.get_running_loop() + self._loop = loop self._buffer = bytearray() self._eof = False # Whether we're done. self._waiter = None # A future used by _wait_for_data() diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 3952b5f2a7743d..32b6e7dd2d826d 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -588,7 +588,7 @@ def as_completed(fs, *, timeout=None): from .queues import Queue # Import here to avoid circular import problem. done = Queue() - loop = events._get_event_loop() + loop = events.get_running_loop() todo = {ensure_future(f, loop=loop) for f in set(fs)} timeout_handle = None @@ -674,7 +674,7 @@ def _ensure_future(coro_or_future, *, loop=None): 'is required') if loop is None: - loop = events._get_event_loop(stacklevel=4) + loop = events.get_running_loop() try: return loop.create_task(coro_or_future) except RuntimeError: @@ -755,7 +755,7 @@ def gather(*coros_or_futures, return_exceptions=False): gather won't cancel any other awaitables. """ if not coros_or_futures: - loop = events._get_event_loop() + loop = events.get_running_loop() outer = loop.create_future() outer.set_result([]) return outer diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index c6671bd0ad3d85..c767db94b5428e 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -747,7 +747,7 @@ async def coro(): def test_env_var_debug(self): code = '\n'.join(( 'import asyncio', - 'loop = asyncio.get_event_loop()', + 'loop = asyncio.new_event_loop()', 'print(loop.get_debug())')) # Test with -E to not fail if the unit test was run with diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 05d9107b28e2ae..1f97d607248bfa 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -2709,15 +2709,11 @@ def get_event_loop(self): asyncio.set_event_loop_policy(Policy()) loop = asyncio.new_event_loop() - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaises(TestError): - asyncio.get_event_loop() - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running'): + asyncio.get_event_loop() asyncio.set_event_loop(None) - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaises(TestError): - asyncio.get_event_loop() - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running'): + asyncio.get_event_loop() with self.assertRaisesRegex(RuntimeError, 'no running'): asyncio.get_running_loop() @@ -2731,16 +2727,11 @@ async def func(): loop.run_until_complete(func()) asyncio.set_event_loop(loop) - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaises(TestError): - asyncio.get_event_loop() - self.assertEqual(cm.filename, __file__) - + with self.assertRaisesRegex(RuntimeError, 'no running'): + asyncio.get_event_loop() asyncio.set_event_loop(None) - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaises(TestError): - asyncio.get_event_loop() - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running'): + asyncio.get_event_loop() finally: asyncio.set_event_loop_policy(old_policy) @@ -2759,15 +2750,11 @@ def test_get_event_loop_returns_running_loop2(self): loop = asyncio.new_event_loop() self.addCleanup(loop.close) - with self.assertWarns(DeprecationWarning) as cm: - loop2 = asyncio.get_event_loop() - self.addCleanup(loop2.close) - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running'): + asyncio.get_event_loop() asyncio.set_event_loop(None) - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaisesRegex(RuntimeError, 'no current'): - asyncio.get_event_loop() - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running'): + asyncio.get_event_loop() with self.assertRaisesRegex(RuntimeError, 'no running'): asyncio.get_running_loop() @@ -2781,15 +2768,11 @@ async def func(): loop.run_until_complete(func()) asyncio.set_event_loop(loop) - with self.assertWarns(DeprecationWarning) as cm: + with self.assertRaisesRegex(RuntimeError, 'no running'): self.assertIs(asyncio.get_event_loop(), loop) - self.assertEqual(cm.filename, __file__) - asyncio.set_event_loop(None) - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaisesRegex(RuntimeError, 'no current'): - asyncio.get_event_loop() - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running'): + asyncio.get_event_loop() finally: asyncio.set_event_loop_policy(old_policy) @@ -2807,7 +2790,7 @@ class TestPyGetEventLoop(GetEventLoopTestsMixin, unittest.TestCase): _get_running_loop_impl = events._py__get_running_loop _set_running_loop_impl = events._py__set_running_loop get_running_loop_impl = events._py_get_running_loop - get_event_loop_impl = events._py_get_event_loop + get_event_loop_impl = events._py_get_running_loop try: @@ -2821,7 +2804,7 @@ class TestCGetEventLoop(GetEventLoopTestsMixin, unittest.TestCase): _get_running_loop_impl = events._c__get_running_loop _set_running_loop_impl = events._c__set_running_loop get_running_loop_impl = events._c_get_running_loop - get_event_loop_impl = events._c_get_event_loop + get_event_loop_impl = events._c_get_running_loop class TestServer(unittest.TestCase): diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index f4a46ec90a16fe..31579a8673d9b0 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -145,10 +145,8 @@ def test_initial_state(self): self.assertTrue(f.cancelled()) def test_constructor_without_loop(self): - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'): - self._new_future() - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + self._new_future() def test_constructor_use_running_loop(self): async def test(): @@ -158,14 +156,11 @@ async def test(): self.assertIs(f.get_loop(), self.loop) def test_constructor_use_global_loop(self): - # Deprecated in 3.10 + # Deprecated in 3.10, error in 3.12 asyncio.set_event_loop(self.loop) self.addCleanup(asyncio.set_event_loop, None) - with self.assertWarns(DeprecationWarning) as cm: - f = self._new_future() - self.assertEqual(cm.filename, __file__) - self.assertIs(f._loop, self.loop) - self.assertIs(f.get_loop(), self.loop) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + self._new_future() def test_constructor_positional(self): # Make sure Future doesn't accept a positional argument @@ -507,10 +502,8 @@ def run(arg): return (arg, threading.get_ident()) ex = concurrent.futures.ThreadPoolExecutor(1) f1 = ex.submit(run, 'oi') - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaises(RuntimeError): - asyncio.wrap_future(f1) - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.wrap_future(f1) ex.shutdown(wait=True) def test_wrap_future_use_running_loop(self): @@ -525,17 +518,15 @@ async def test(): ex.shutdown(wait=True) def test_wrap_future_use_global_loop(self): - # Deprecated in 3.10 + # Deprecated in 3.10, error in 3.12 asyncio.set_event_loop(self.loop) self.addCleanup(asyncio.set_event_loop, None) def run(arg): return (arg, threading.get_ident()) ex = concurrent.futures.ThreadPoolExecutor(1) f1 = ex.submit(run, 'oi') - with self.assertWarns(DeprecationWarning) as cm: - f2 = asyncio.wrap_future(f1) - self.assertEqual(cm.filename, __file__) - self.assertIs(self.loop, f2._loop) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.wrap_future(f1) ex.shutdown(wait=True) def test_wrap_future_cancel(self): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 098a0da344d0fb..55275970dec936 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -810,10 +810,8 @@ def test_read_all_from_pipe_reader(self): self.assertEqual(data, b'data') def test_streamreader_constructor_without_loop(self): - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'): - asyncio.StreamReader() - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.StreamReader() def test_streamreader_constructor_use_running_loop(self): # asyncio issue #184: Ensure that StreamReaderProtocol constructor @@ -827,21 +825,17 @@ async def test(): def test_streamreader_constructor_use_global_loop(self): # asyncio issue #184: Ensure that StreamReaderProtocol constructor # retrieves the current loop if the loop parameter is not set - # Deprecated in 3.10 + # Deprecated in 3.10, error in 3.12 self.addCleanup(asyncio.set_event_loop, None) asyncio.set_event_loop(self.loop) - with self.assertWarns(DeprecationWarning) as cm: - reader = asyncio.StreamReader() - self.assertEqual(cm.filename, __file__) - self.assertIs(reader._loop, self.loop) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.StreamReader() def test_streamreaderprotocol_constructor_without_loop(self): reader = mock.Mock() - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'): - asyncio.StreamReaderProtocol(reader) - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.StreamReaderProtocol(reader) def test_streamreaderprotocol_constructor_use_running_loop(self): # asyncio issue #184: Ensure that StreamReaderProtocol constructor @@ -855,14 +849,12 @@ async def test(): def test_streamreaderprotocol_constructor_use_global_loop(self): # asyncio issue #184: Ensure that StreamReaderProtocol constructor # retrieves the current loop if the loop parameter is not set - # Deprecated in 3.10 + # Deprecated in 3.10, error in 3.12 self.addCleanup(asyncio.set_event_loop, None) asyncio.set_event_loop(self.loop) reader = mock.Mock() - with self.assertWarns(DeprecationWarning) as cm: - protocol = asyncio.StreamReaderProtocol(reader) - self.assertEqual(cm.filename, __file__) - self.assertIs(protocol._loop, self.loop) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.StreamReaderProtocol(reader) def test_drain_raises(self): # See http://bugs.python.org/issue25441 diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 6458859db2d127..74f10c439815de 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -208,10 +208,8 @@ async def notmuch(): a = notmuch() self.addCleanup(a.close) - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'): - asyncio.ensure_future(a) - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.ensure_future(a) async def test(): return asyncio.ensure_future(notmuch()) @@ -221,16 +219,13 @@ async def test(): self.assertTrue(t.done()) self.assertEqual(t.result(), 'ok') - # Deprecated in 3.10 + # Deprecated in 3.10, error in 3.12 asyncio.set_event_loop(self.loop) self.addCleanup(asyncio.set_event_loop, None) - with self.assertWarns(DeprecationWarning) as cm: - t = asyncio.ensure_future(notmuch()) - self.assertEqual(cm.filename, __file__) - self.assertIs(t._loop, self.loop) - self.loop.run_until_complete(t) - self.assertTrue(t.done()) - self.assertEqual(t.result(), 'ok') + a = notmuch() + self.addCleanup(a.close) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.ensure_future(a) def test_ensure_future_future(self): f_orig = self.new_future(self.loop) @@ -1446,10 +1441,8 @@ async def coro(): self.addCleanup(a.close) futs = asyncio.as_completed([a]) - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'): - list(futs) - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + list(futs) def test_as_completed_coroutine_use_running_loop(self): loop = self.new_test_loop() @@ -1878,10 +1871,8 @@ async def coro(): inner = coro() self.addCleanup(inner.close) - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'): - asyncio.shield(inner) - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.shield(inner) def test_shield_coroutine_use_running_loop(self): async def coro(): @@ -1895,18 +1886,16 @@ async def test(): self.assertEqual(res, 42) def test_shield_coroutine_use_global_loop(self): - # Deprecated in 3.10 + # Deprecated in 3.10, error in 3.12 async def coro(): return 42 asyncio.set_event_loop(self.loop) self.addCleanup(asyncio.set_event_loop, None) - with self.assertWarns(DeprecationWarning) as cm: - outer = asyncio.shield(coro()) - self.assertEqual(cm.filename, __file__) - self.assertEqual(outer._loop, self.loop) - res = self.loop.run_until_complete(outer) - self.assertEqual(res, 42) + c = coro() + self.addCleanup(c.close) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.shield(c) def test_as_completed_invalid_args(self): fut = self.new_future(self.loop) @@ -2744,7 +2733,7 @@ def test_current_task_no_running_loop(self): self.assertIsNone(asyncio.current_task(loop=self.loop)) def test_current_task_no_running_loop_implicit(self): - with self.assertRaises(RuntimeError): + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): asyncio.current_task() def test_current_task_with_implicit_loop(self): @@ -2908,10 +2897,8 @@ def _gather(self, *args, **kwargs): return asyncio.gather(*args, **kwargs) def test_constructor_empty_sequence_without_loop(self): - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaises(RuntimeError): - asyncio.gather() - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.gather() def test_constructor_empty_sequence_use_running_loop(self): async def gather(): @@ -2924,17 +2911,11 @@ async def gather(): self.assertEqual(fut.result(), []) def test_constructor_empty_sequence_use_global_loop(self): - # Deprecated in 3.10 + # Deprecated in 3.10, error in 3.12 asyncio.set_event_loop(self.one_loop) self.addCleanup(asyncio.set_event_loop, None) - with self.assertWarns(DeprecationWarning) as cm: - fut = asyncio.gather() - self.assertEqual(cm.filename, __file__) - self.assertIsInstance(fut, asyncio.Future) - self.assertIs(fut._loop, self.one_loop) - self._run_loop(self.one_loop) - self.assertTrue(fut.done()) - self.assertEqual(fut.result(), []) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.gather() def test_constructor_heterogenous_futures(self): fut1 = self.one_loop.create_future() @@ -3017,10 +2998,8 @@ async def coro(): self.addCleanup(gen1.close) gen2 = coro() self.addCleanup(gen2.close) - with self.assertWarns(DeprecationWarning) as cm: - with self.assertRaises(RuntimeError): - asyncio.gather(gen1, gen2) - self.assertEqual(cm.filename, __file__) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.gather(gen1, gen2) def test_constructor_use_running_loop(self): async def coro(): @@ -3034,18 +3013,17 @@ async def gather(): self.one_loop.run_until_complete(fut) def test_constructor_use_global_loop(self): - # Deprecated in 3.10 + # Deprecated in 3.10, error in 3.12 async def coro(): return 'abc' asyncio.set_event_loop(self.other_loop) self.addCleanup(asyncio.set_event_loop, None) gen1 = coro() gen2 = coro() - with self.assertWarns(DeprecationWarning) as cm: - fut = asyncio.gather(gen1, gen2) - self.assertEqual(cm.filename, __file__) - self.assertIs(fut._loop, self.other_loop) - self.other_loop.run_until_complete(fut) + with self.assertRaisesRegex(RuntimeError, 'no running event loop'): + asyncio.gather(gen1, gen2) + self.other_loop.run_until_complete(gen1) + self.other_loop.run_until_complete(gen2) def test_duplicate_coroutines(self): async def coro(s): diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index dba5ceffaf1c03..c5d5e71688ce97 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2387,7 +2387,8 @@ class UnawaitedWarningDuringShutdownTest(unittest.TestCase): def test_unawaited_warning_during_shutdown(self): code = ("import asyncio\n" "async def f(): pass\n" - "asyncio.gather(f())\n") + "async def t(): asyncio.gather(f())\n" + "asyncio.run(t())\n") assert_python_ok("-c", code) code = ("import sys\n" diff --git a/Misc/NEWS.d/next/Library/2022-06-03-10-32-54.gh-issue-93453.M3583s.rst b/Misc/NEWS.d/next/Library/2022-06-03-10-32-54.gh-issue-93453.M3583s.rst new file mode 100644 index 00000000000000..94cd290e9131cf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-06-03-10-32-54.gh-issue-93453.M3583s.rst @@ -0,0 +1,2 @@ +Make :func:`asyncio.get_event_loop` an alias of +:func:`asyncio.get_running_loop`. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index bb7c5a7c3ec550..6b57714cc8a098 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -21,7 +21,6 @@ _Py_IDENTIFIER(_asyncio_future_blocking); _Py_IDENTIFIER(add_done_callback); _Py_IDENTIFIER(call_soon); _Py_IDENTIFIER(cancel); -_Py_IDENTIFIER(get_event_loop); _Py_IDENTIFIER(throw); _Py_IDENTIFIER(_check_future); @@ -326,37 +325,6 @@ set_running_loop(PyObject *loop) } -static PyObject * -get_event_loop(int stacklevel) -{ - PyObject *loop; - PyObject *policy; - - if (get_running_loop(&loop)) { - return NULL; - } - if (loop != NULL) { - return loop; - } - - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "There is no current event loop", - stacklevel)) - { - return NULL; - } - - policy = PyObject_CallNoArgs(asyncio_get_event_loop_policy); - if (policy == NULL) { - return NULL; - } - - loop = _PyObject_CallMethodIdNoArgs(policy, &PyId_get_event_loop); - Py_DECREF(policy); - return loop; -} - - static int call_soon(PyObject *loop, PyObject *func, PyObject *arg, PyObject *ctx) { @@ -504,8 +472,13 @@ future_init(FutureObj *fut, PyObject *loop) fut->fut_blocking = 0; if (loop == Py_None) { - loop = get_event_loop(1); + if (get_running_loop(&loop)) { + return -1; + } if (loop == NULL) { + /* There's no currently running event loop */ + PyErr_SetString( + PyExc_RuntimeError, "no running event loop"); return -1; } } @@ -3163,38 +3136,6 @@ _asyncio__set_running_loop(PyObject *module, PyObject *loop) Py_RETURN_NONE; } -/*[clinic input] -_asyncio.get_event_loop - -Return an asyncio event loop. - -When called from a coroutine or a callback (e.g. scheduled with -call_soon or similar API), this function will always return the -running event loop. - -If there is no running event loop set, the function will return -the result of `get_event_loop_policy().get_event_loop()` call. -[clinic start generated code]*/ - -static PyObject * -_asyncio_get_event_loop_impl(PyObject *module) -/*[clinic end generated code: output=2a2d8b2f824c648b input=9364bf2916c8655d]*/ -{ - return get_event_loop(1); -} - -/*[clinic input] -_asyncio._get_event_loop - stacklevel: int = 3 -[clinic start generated code]*/ - -static PyObject * -_asyncio__get_event_loop_impl(PyObject *module, int stacklevel) -/*[clinic end generated code: output=9c1d6d3c802e67c9 input=d17aebbd686f711d]*/ -{ - return get_event_loop(stacklevel-1); -} - /*[clinic input] _asyncio.get_running_loop @@ -3489,8 +3430,6 @@ module_init(void) PyDoc_STRVAR(module_doc, "Accelerator module for asyncio"); static PyMethodDef asyncio_methods[] = { - _ASYNCIO_GET_EVENT_LOOP_METHODDEF - _ASYNCIO__GET_EVENT_LOOP_METHODDEF _ASYNCIO_GET_RUNNING_LOOP_METHODDEF _ASYNCIO__GET_RUNNING_LOOP_METHODDEF _ASYNCIO__SET_RUNNING_LOOP_METHODDEF diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index 7cc27b8289598d..aba6f1d5dca3de 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -700,70 +700,6 @@ PyDoc_STRVAR(_asyncio__set_running_loop__doc__, #define _ASYNCIO__SET_RUNNING_LOOP_METHODDEF \ {"_set_running_loop", (PyCFunction)_asyncio__set_running_loop, METH_O, _asyncio__set_running_loop__doc__}, -PyDoc_STRVAR(_asyncio_get_event_loop__doc__, -"get_event_loop($module, /)\n" -"--\n" -"\n" -"Return an asyncio event loop.\n" -"\n" -"When called from a coroutine or a callback (e.g. scheduled with\n" -"call_soon or similar API), this function will always return the\n" -"running event loop.\n" -"\n" -"If there is no running event loop set, the function will return\n" -"the result of `get_event_loop_policy().get_event_loop()` call."); - -#define _ASYNCIO_GET_EVENT_LOOP_METHODDEF \ - {"get_event_loop", (PyCFunction)_asyncio_get_event_loop, METH_NOARGS, _asyncio_get_event_loop__doc__}, - -static PyObject * -_asyncio_get_event_loop_impl(PyObject *module); - -static PyObject * -_asyncio_get_event_loop(PyObject *module, PyObject *Py_UNUSED(ignored)) -{ - return _asyncio_get_event_loop_impl(module); -} - -PyDoc_STRVAR(_asyncio__get_event_loop__doc__, -"_get_event_loop($module, /, stacklevel=3)\n" -"--\n" -"\n"); - -#define _ASYNCIO__GET_EVENT_LOOP_METHODDEF \ - {"_get_event_loop", _PyCFunction_CAST(_asyncio__get_event_loop), METH_FASTCALL|METH_KEYWORDS, _asyncio__get_event_loop__doc__}, - -static PyObject * -_asyncio__get_event_loop_impl(PyObject *module, int stacklevel); - -static PyObject * -_asyncio__get_event_loop(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - static const char * const _keywords[] = {"stacklevel", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "_get_event_loop", 0}; - PyObject *argsbuf[1]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - int stacklevel = 3; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - stacklevel = _PyLong_AsInt(args[0]); - if (stacklevel == -1 && PyErr_Occurred()) { - goto exit; - } -skip_optional_pos: - return_value = _asyncio__get_event_loop_impl(module, stacklevel); - -exit: - return return_value; -} - PyDoc_STRVAR(_asyncio_get_running_loop__doc__, "get_running_loop($module, /)\n" "--\n" @@ -927,4 +863,4 @@ _asyncio__leave_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs, exit: return return_value; } -/*[clinic end generated code: output=eccf150c9c30efd5 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1e3d18857fca365d input=a9049054013a1b77]*/ From b4a16bc3c526dcabcfa2bd6b46e3239fa86df084 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 19 Oct 2022 09:55:14 +0300 Subject: [PATCH 2/2] Remove duplicated assignment. --- Lib/asyncio/events.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index a3e11dbca25d3c..70dc59bb766f9f 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -775,9 +775,6 @@ def set_event_loop_policy(policy): _event_loop_policy = policy -get_event_loop = get_running_loop - - def set_event_loop(loop): """Equivalent to calling get_event_loop_policy().set_event_loop(loop).""" get_event_loop_policy().set_event_loop(loop)