Skip to content

Commit

Permalink
[3.10] gh-93453: Only emit deprecation warning in asyncio.get_event_l…
Browse files Browse the repository at this point in the history
…oop when a new event loop is created (#100059)

It no longer emits a deprecation warning if the current event loop was set.

(cherry picked from commit 3fae04b)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
  • Loading branch information
ambv and serhiy-storchaka committed Dec 6, 2022
1 parent b7ae1d2 commit 300d812
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 136 deletions.
22 changes: 14 additions & 8 deletions Doc/library/asyncio-eventloop.rst
Expand Up @@ -43,10 +43,12 @@ an event loop:

Get the current event 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.
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.

Because this function has rather complex behavior (especially
when custom event loop policies are in use), using the
Expand All @@ -58,10 +60,14 @@ an event loop:
event loop.

.. deprecated:: 3.10
Emits a deprecation warning if there is no running event loop.
In future Python releases, this function may become an alias of
:func:`get_running_loop` and will accordingly raise a
:exc:`RuntimeError` if there is no running event loop.
Deprecation warning is emitted if there is no current event loop.
In Python 3.12 it will be an error.

.. note::
In Python versions 3.10.0--3.10.8 this function
(and other functions which used it implicitly) emitted a
:exc:`DeprecationWarning` if there was no running event loop, even if
the current loop was set.

.. function:: set_event_loop(loop)

Expand Down
2 changes: 1 addition & 1 deletion Doc/library/asyncio-llapi-index.rst
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).
- Get an event loop instance (running or current via the current policy).

* - :func:`asyncio.set_event_loop`
- Set the event loop as current via the current policy.
Expand Down
5 changes: 5 additions & 0 deletions Doc/library/asyncio-policy.rst
Expand Up @@ -112,6 +112,11 @@ asyncio ships with the following built-in policies:

On Windows, :class:`ProactorEventLoop` is now used by default.

.. deprecated:: 3.10.9
:meth:`get_event_loop` now emits a :exc:`DeprecationWarning` if there
is no current event loop set and a new event loop has been implicitly
created. In Python 3.12 it will be an error.


.. class:: WindowsSelectorEventLoopPolicy

Expand Down
22 changes: 19 additions & 3 deletions Lib/asyncio/events.py
Expand Up @@ -650,6 +650,21 @@ def get_event_loop(self):
if (self._local._loop is None and
not self._local._set_called and
threading.current_thread() is threading.main_thread()):
stacklevel = 2
try:
f = sys._getframe(1)
except AttributeError:
pass
else:
while f:
module = f.f_globals.get('__name__')
if not (module == 'asyncio' or module.startswith('asyncio.')):
break
f = f.f_back
stacklevel += 1
import warnings
warnings.warn('There is no current event loop',
DeprecationWarning, stacklevel=stacklevel)
self.set_event_loop(self.new_event_loop())

if self._local._loop is None:
Expand Down Expand Up @@ -763,12 +778,13 @@ def get_event_loop():


def _get_event_loop(stacklevel=3):
# This internal method is going away in Python 3.12, left here only for
# backwards compatibility with 3.10.0 - 3.10.8 and 3.11.0.
# Similarly, this method's C equivalent in _asyncio is going away as well.
# See GH-99949 for more details.
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()


Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_asyncio/test_base_events.py
Expand Up @@ -752,7 +752,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
54 changes: 22 additions & 32 deletions Lib/test/test_asyncio/test_events.py
Expand Up @@ -2561,8 +2561,9 @@ def test_event_loop_policy(self):
def test_get_event_loop(self):
policy = asyncio.DefaultEventLoopPolicy()
self.assertIsNone(policy._local._loop)

loop = policy.get_event_loop()
with self.assertWarns(DeprecationWarning) as cm:
loop = policy.get_event_loop()
self.assertEqual(cm.filename, __file__)
self.assertIsInstance(loop, asyncio.AbstractEventLoop)

self.assertIs(policy._local._loop, loop)
Expand All @@ -2576,7 +2577,10 @@ def test_get_event_loop_calls_set_event_loop(self):
policy, "set_event_loop",
wraps=policy.set_event_loop) as m_set_event_loop:

loop = policy.get_event_loop()
with self.assertWarns(DeprecationWarning) as cm:
loop = policy.get_event_loop()
self.addCleanup(loop.close)
self.assertEqual(cm.filename, __file__)

# policy._local._loop must be set through .set_event_loop()
# (the unix DefaultEventLoopPolicy needs this call to attach
Expand Down Expand Up @@ -2610,7 +2614,8 @@ def test_new_event_loop(self):

def test_set_event_loop(self):
policy = asyncio.DefaultEventLoopPolicy()
old_loop = policy.get_event_loop()
old_loop = policy.new_event_loop()
policy.set_event_loop(old_loop)

self.assertRaises(AssertionError, policy.set_event_loop, object())

Expand Down Expand Up @@ -2723,15 +2728,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.warnings[0].filename, __file__)
with self.assertRaises(TestError):
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.warnings[0].filename, __file__)
with self.assertRaises(TestError):
asyncio.get_event_loop()

with self.assertRaisesRegex(RuntimeError, 'no running'):
asyncio.get_running_loop()
Expand All @@ -2745,16 +2746,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.warnings[0].filename, __file__)

with self.assertRaises(TestError):
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.warnings[0].filename, __file__)
with self.assertRaises(TestError):
asyncio.get_event_loop()

finally:
asyncio.set_event_loop_policy(old_policy)
Expand All @@ -2778,10 +2774,8 @@ def test_get_event_loop_returns_running_loop2(self):
self.addCleanup(loop2.close)
self.assertEqual(cm.warnings[0].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.warnings[0].filename, __file__)
with self.assertRaisesRegex(RuntimeError, 'no current'):
asyncio.get_event_loop()

with self.assertRaisesRegex(RuntimeError, 'no running'):
asyncio.get_running_loop()
Expand All @@ -2795,15 +2789,11 @@ async def func():
loop.run_until_complete(func())

asyncio.set_event_loop(loop)
with self.assertWarns(DeprecationWarning) as cm:
self.assertIs(asyncio.get_event_loop(), loop)
self.assertEqual(cm.warnings[0].filename, __file__)
self.assertIs(asyncio.get_event_loop(), 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.warnings[0].filename, __file__)
with self.assertRaisesRegex(RuntimeError, 'no current'):
asyncio.get_event_loop()

finally:
asyncio.set_event_loop_policy(old_policy)
Expand Down
24 changes: 8 additions & 16 deletions Lib/test/test_asyncio/test_futures.py
Expand Up @@ -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.warnings[0].filename, __file__)
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
self._new_future()

def test_constructor_use_running_loop(self):
async def test():
Expand All @@ -158,12 +156,10 @@ 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, undeprecated in 3.11.1
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.warnings[0].filename, __file__)
f = self._new_future()
self.assertIs(f._loop, self.loop)
self.assertIs(f.get_loop(), self.loop)

Expand Down Expand Up @@ -499,10 +495,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.warnings[0].filename, __file__)
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
asyncio.wrap_future(f1)
ex.shutdown(wait=True)

def test_wrap_future_use_running_loop(self):
Expand All @@ -517,16 +511,14 @@ async def test():
ex.shutdown(wait=True)

def test_wrap_future_use_global_loop(self):
# Deprecated in 3.10
# Deprecated in 3.10, undeprecated in 3.11.1
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.warnings[0].filename, __file__)
f2 = asyncio.wrap_future(f1)
self.assertIs(self.loop, f2._loop)
ex.shutdown(wait=True)

Expand Down
24 changes: 8 additions & 16 deletions Lib/test/test_asyncio/test_streams.py
Expand Up @@ -747,10 +747,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.warnings[0].filename, __file__)
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
asyncio.StreamReader()

def test_streamreader_constructor_use_running_loop(self):
# asyncio issue #184: Ensure that StreamReaderProtocol constructor
Expand All @@ -764,21 +762,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, undeprecated in 3.11.1
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.warnings[0].filename, __file__)
reader = asyncio.StreamReader()
self.assertIs(reader._loop, self.loop)


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.warnings[0].filename, __file__)
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
asyncio.StreamReaderProtocol(reader)

def test_streamreaderprotocol_constructor_use_running_loop(self):
# asyncio issue #184: Ensure that StreamReaderProtocol constructor
Expand All @@ -792,13 +786,11 @@ 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, undeprecated in 3.11.1
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.warnings[0].filename, __file__)
protocol = asyncio.StreamReaderProtocol(reader)
self.assertIs(protocol._loop, self.loop)

def test_multiple_drain(self):
Expand Down

0 comments on commit 300d812

Please sign in to comment.