[3.11] gh-93453: Only emit deprecation warning in asyncio.get_event_loop when a new event loop is created by serhiy-storchaka · Pull Request #99949 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
22 changes: 14 additions & 8 deletions Doc/library/asyncio-eventloop.rst
2 changes: 1 addition & 1 deletion 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).
- 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
Original file line number Diff line number Diff line change
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.11.1
: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
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,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 @@ -786,12 +801,13 @@ def get_event_loop():


def _get_event_loop(stacklevel=3):
Comment thread
ambv marked this conversation as resolved.
# 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
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
54 changes: 22 additions & 32 deletions Lib/test/test_asyncio/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2547,8 +2547,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 @@ -2562,7 +2563,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 @@ -2596,7 +2600,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(TypeError, policy.set_event_loop, object())

Expand Down Expand Up @@ -2709,15 +2714,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.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.filename, __file__)
with self.assertRaises(TestError):
asyncio.get_event_loop()

with self.assertRaisesRegex(RuntimeError, 'no running'):
asyncio.get_running_loop()
Expand All @@ -2731,16 +2732,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.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.filename, __file__)
with self.assertRaises(TestError):
asyncio.get_event_loop()

finally:
asyncio.set_event_loop_policy(old_policy)
Expand All @@ -2764,10 +2760,8 @@ def test_get_event_loop_returns_running_loop2(self):
self.addCleanup(loop2.close)
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 current'):
asyncio.get_event_loop()

with self.assertRaisesRegex(RuntimeError, 'no running'):
asyncio.get_running_loop()
Expand All @@ -2781,15 +2775,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.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.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
Original file line number Diff line number Diff line change
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.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.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.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.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
Loading