Skip to content

Commit

Permalink
gh-93453: Make get_event_loop() an alias of get_running_loop()
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiy-storchaka committed Jun 3, 2022
1 parent b013804 commit 64dcf5b
Show file tree
Hide file tree
Showing 16 changed files with 105 additions and 321 deletions.
31 changes: 9 additions & 22 deletions Doc/library/asyncio-eventloop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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 <asyncio-policies>`.

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 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 @@ -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()
<asyncio_example_lowlevel_helloworld>`.

* :ref:`Using loop.call_later() <asyncio_example_call_later>`.
Expand Down Expand Up @@ -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 <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 @@ -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
Expand Down
30 changes: 4 additions & 26 deletions Lib/asyncio/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -820,22 +800,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
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 @@ -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
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_waiter = None
self._connection_lost = False
Expand Down Expand Up @@ -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()
Expand Down
6 changes: 3 additions & 3 deletions Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
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 @@ -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
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 @@ -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()
Expand All @@ -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)
Expand All @@ -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()
Expand All @@ -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)
Expand All @@ -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:
Expand All @@ -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):
Expand Down
Loading

0 comments on commit 64dcf5b

Please sign in to comment.