Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-93453: Make get_event_loop() an alias of get_running_loop() #93454

Closed
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 6 additions & 19 deletions Doc/library/asyncio-eventloop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,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)

Expand All @@ -68,7 +55,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 <asyncio-policies>`.

Expand Down
4 changes: 2 additions & 2 deletions Doc/library/asyncio-llapi-index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -503,7 +503,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 <asyncio-policies>` for more
details.

Expand Down
2 changes: 1 addition & 1 deletion Doc/library/asyncio-policy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ This is per-thread by default,
though custom policies could define *context* differently.

Custom event loop policies can control the behavior of
:func:`get_event_loop`, :func:`set_event_loop`, and :func:`new_event_loop`.
:func:`set_event_loop` and :func:`new_event_loop`.

Policy objects should implement the APIs defined
in the :class:`AbstractEventLoopPolicy` abstract base class.
Expand Down
30 changes: 4 additions & 26 deletions Lib/asyncio/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,27 +775,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):
Expand Down Expand Up @@ -823,22 +803,20 @@ 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:
# get_event_loop() is one of the most frequently called
# 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:
# Alias C implementations for testing purposes.
_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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a duplicate of line 778?

7 changes: 3 additions & 4 deletions Lib/asyncio/futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -419,7 +418,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
Expand Down
10 changes: 4 additions & 6 deletions Lib/asyncio/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -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_waiters = collections.deque()
self._connection_lost = False
Expand Down Expand Up @@ -404,9 +403,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()
Expand Down
6 changes: 3 additions & 3 deletions Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,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

Expand Down Expand Up @@ -673,7 +673,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:
Expand Down Expand Up @@ -754,7 +754,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
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_asyncio/test_base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,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
Expand Down
51 changes: 17 additions & 34 deletions Lib/test/test_asyncio/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2708,15 +2708,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()
Expand All @@ -2730,16 +2726,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)
Expand All @@ -2758,15 +2749,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()
Expand All @@ -2780,15 +2767,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)
Expand All @@ -2806,7 +2789,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:
Expand All @@ -2820,7 +2803,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):
Expand Down
29 changes: 10 additions & 19 deletions Lib/test/test_asyncio/test_futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,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():
Expand All @@ -159,14 +157,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
Expand Down Expand Up @@ -508,10 +503,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):
Expand All @@ -526,17 +519,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):
Expand Down
Loading