Skip to content

Commit 300d812

Browse files
[3.10] gh-93453: Only emit deprecation warning in asyncio.get_event_loop 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 <[email protected]> Co-authored-by: Łukasz Langa <[email protected]>
1 parent b7ae1d2 commit 300d812

13 files changed

+115
-136
lines changed

Doc/library/asyncio-eventloop.rst

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ an event loop:
4343

4444
Get the current event loop.
4545

46-
If there is no current event loop set in the current OS thread,
47-
the OS thread is main, and :func:`set_event_loop` has not yet
48-
been called, asyncio will create a new event loop and set it as the
49-
current one.
46+
When called from a coroutine or a callback (e.g. scheduled with
47+
call_soon or similar API), this function will always return the
48+
running event loop.
49+
50+
If there is no running event loop set, the function will return
51+
the result of ``get_event_loop_policy().get_event_loop()`` call.
5052

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

6062
.. deprecated:: 3.10
61-
Emits a deprecation warning if there is no running event loop.
62-
In future Python releases, this function may become an alias of
63-
:func:`get_running_loop` and will accordingly raise a
64-
:exc:`RuntimeError` if there is no running event loop.
63+
Deprecation warning is emitted if there is no current event loop.
64+
In Python 3.12 it will be an error.
65+
66+
.. note::
67+
In Python versions 3.10.0--3.10.8 this function
68+
(and other functions which used it implicitly) emitted a
69+
:exc:`DeprecationWarning` if there was no running event loop, even if
70+
the current loop was set.
6571

6672
.. function:: set_event_loop(loop)
6773

Doc/library/asyncio-llapi-index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Obtaining the Event Loop
1919
- The **preferred** function to get the running event loop.
2020

2121
* - :func:`asyncio.get_event_loop`
22-
- Get an event loop instance (current or via the policy).
22+
- Get an event loop instance (running or current via the current policy).
2323

2424
* - :func:`asyncio.set_event_loop`
2525
- Set the event loop as current via the current policy.

Doc/library/asyncio-policy.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ asyncio ships with the following built-in policies:
112112

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

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

116121
.. class:: WindowsSelectorEventLoopPolicy
117122

Lib/asyncio/events.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,21 @@ def get_event_loop(self):
650650
if (self._local._loop is None and
651651
not self._local._set_called and
652652
threading.current_thread() is threading.main_thread()):
653+
stacklevel = 2
654+
try:
655+
f = sys._getframe(1)
656+
except AttributeError:
657+
pass
658+
else:
659+
while f:
660+
module = f.f_globals.get('__name__')
661+
if not (module == 'asyncio' or module.startswith('asyncio.')):
662+
break
663+
f = f.f_back
664+
stacklevel += 1
665+
import warnings
666+
warnings.warn('There is no current event loop',
667+
DeprecationWarning, stacklevel=stacklevel)
653668
self.set_event_loop(self.new_event_loop())
654669

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

764779

765780
def _get_event_loop(stacklevel=3):
781+
# This internal method is going away in Python 3.12, left here only for
782+
# backwards compatibility with 3.10.0 - 3.10.8 and 3.11.0.
783+
# Similarly, this method's C equivalent in _asyncio is going away as well.
784+
# See GH-99949 for more details.
766785
current_loop = _get_running_loop()
767786
if current_loop is not None:
768787
return current_loop
769-
import warnings
770-
warnings.warn('There is no current event loop',
771-
DeprecationWarning, stacklevel=stacklevel)
772788
return get_event_loop_policy().get_event_loop()
773789

774790

Lib/test/test_asyncio/test_base_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ async def coro():
752752
def test_env_var_debug(self):
753753
code = '\n'.join((
754754
'import asyncio',
755-
'loop = asyncio.get_event_loop()',
755+
'loop = asyncio.new_event_loop()',
756756
'print(loop.get_debug())'))
757757

758758
# Test with -E to not fail if the unit test was run with

Lib/test/test_asyncio/test_events.py

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2561,8 +2561,9 @@ def test_event_loop_policy(self):
25612561
def test_get_event_loop(self):
25622562
policy = asyncio.DefaultEventLoopPolicy()
25632563
self.assertIsNone(policy._local._loop)
2564-
2565-
loop = policy.get_event_loop()
2564+
with self.assertWarns(DeprecationWarning) as cm:
2565+
loop = policy.get_event_loop()
2566+
self.assertEqual(cm.filename, __file__)
25662567
self.assertIsInstance(loop, asyncio.AbstractEventLoop)
25672568

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

2579-
loop = policy.get_event_loop()
2580+
with self.assertWarns(DeprecationWarning) as cm:
2581+
loop = policy.get_event_loop()
2582+
self.addCleanup(loop.close)
2583+
self.assertEqual(cm.filename, __file__)
25802584

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

26112615
def test_set_event_loop(self):
26122616
policy = asyncio.DefaultEventLoopPolicy()
2613-
old_loop = policy.get_event_loop()
2617+
old_loop = policy.new_event_loop()
2618+
policy.set_event_loop(old_loop)
26142619

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

@@ -2723,15 +2728,11 @@ def get_event_loop(self):
27232728
asyncio.set_event_loop_policy(Policy())
27242729
loop = asyncio.new_event_loop()
27252730

2726-
with self.assertWarns(DeprecationWarning) as cm:
2727-
with self.assertRaises(TestError):
2728-
asyncio.get_event_loop()
2729-
self.assertEqual(cm.warnings[0].filename, __file__)
2731+
with self.assertRaises(TestError):
2732+
asyncio.get_event_loop()
27302733
asyncio.set_event_loop(None)
2731-
with self.assertWarns(DeprecationWarning) as cm:
2732-
with self.assertRaises(TestError):
2733-
asyncio.get_event_loop()
2734-
self.assertEqual(cm.warnings[0].filename, __file__)
2734+
with self.assertRaises(TestError):
2735+
asyncio.get_event_loop()
27352736

27362737
with self.assertRaisesRegex(RuntimeError, 'no running'):
27372738
asyncio.get_running_loop()
@@ -2745,16 +2746,11 @@ async def func():
27452746
loop.run_until_complete(func())
27462747

27472748
asyncio.set_event_loop(loop)
2748-
with self.assertWarns(DeprecationWarning) as cm:
2749-
with self.assertRaises(TestError):
2750-
asyncio.get_event_loop()
2751-
self.assertEqual(cm.warnings[0].filename, __file__)
2752-
2749+
with self.assertRaises(TestError):
2750+
asyncio.get_event_loop()
27532751
asyncio.set_event_loop(None)
2754-
with self.assertWarns(DeprecationWarning) as cm:
2755-
with self.assertRaises(TestError):
2756-
asyncio.get_event_loop()
2757-
self.assertEqual(cm.warnings[0].filename, __file__)
2752+
with self.assertRaises(TestError):
2753+
asyncio.get_event_loop()
27582754

27592755
finally:
27602756
asyncio.set_event_loop_policy(old_policy)
@@ -2778,10 +2774,8 @@ def test_get_event_loop_returns_running_loop2(self):
27782774
self.addCleanup(loop2.close)
27792775
self.assertEqual(cm.warnings[0].filename, __file__)
27802776
asyncio.set_event_loop(None)
2781-
with self.assertWarns(DeprecationWarning) as cm:
2782-
with self.assertRaisesRegex(RuntimeError, 'no current'):
2783-
asyncio.get_event_loop()
2784-
self.assertEqual(cm.warnings[0].filename, __file__)
2777+
with self.assertRaisesRegex(RuntimeError, 'no current'):
2778+
asyncio.get_event_loop()
27852779

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

27972791
asyncio.set_event_loop(loop)
2798-
with self.assertWarns(DeprecationWarning) as cm:
2799-
self.assertIs(asyncio.get_event_loop(), loop)
2800-
self.assertEqual(cm.warnings[0].filename, __file__)
2792+
self.assertIs(asyncio.get_event_loop(), loop)
28012793

28022794
asyncio.set_event_loop(None)
2803-
with self.assertWarns(DeprecationWarning) as cm:
2804-
with self.assertRaisesRegex(RuntimeError, 'no current'):
2805-
asyncio.get_event_loop()
2806-
self.assertEqual(cm.warnings[0].filename, __file__)
2795+
with self.assertRaisesRegex(RuntimeError, 'no current'):
2796+
asyncio.get_event_loop()
28072797

28082798
finally:
28092799
asyncio.set_event_loop_policy(old_policy)

Lib/test/test_asyncio/test_futures.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,8 @@ def test_initial_state(self):
145145
self.assertTrue(f.cancelled())
146146

147147
def test_constructor_without_loop(self):
148-
with self.assertWarns(DeprecationWarning) as cm:
149-
with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'):
150-
self._new_future()
151-
self.assertEqual(cm.warnings[0].filename, __file__)
148+
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
149+
self._new_future()
152150

153151
def test_constructor_use_running_loop(self):
154152
async def test():
@@ -158,12 +156,10 @@ async def test():
158156
self.assertIs(f.get_loop(), self.loop)
159157

160158
def test_constructor_use_global_loop(self):
161-
# Deprecated in 3.10
159+
# Deprecated in 3.10, undeprecated in 3.11.1
162160
asyncio.set_event_loop(self.loop)
163161
self.addCleanup(asyncio.set_event_loop, None)
164-
with self.assertWarns(DeprecationWarning) as cm:
165-
f = self._new_future()
166-
self.assertEqual(cm.warnings[0].filename, __file__)
162+
f = self._new_future()
167163
self.assertIs(f._loop, self.loop)
168164
self.assertIs(f.get_loop(), self.loop)
169165

@@ -499,10 +495,8 @@ def run(arg):
499495
return (arg, threading.get_ident())
500496
ex = concurrent.futures.ThreadPoolExecutor(1)
501497
f1 = ex.submit(run, 'oi')
502-
with self.assertWarns(DeprecationWarning) as cm:
503-
with self.assertRaises(RuntimeError):
504-
asyncio.wrap_future(f1)
505-
self.assertEqual(cm.warnings[0].filename, __file__)
498+
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
499+
asyncio.wrap_future(f1)
506500
ex.shutdown(wait=True)
507501

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

519513
def test_wrap_future_use_global_loop(self):
520-
# Deprecated in 3.10
514+
# Deprecated in 3.10, undeprecated in 3.11.1
521515
asyncio.set_event_loop(self.loop)
522516
self.addCleanup(asyncio.set_event_loop, None)
523517
def run(arg):
524518
return (arg, threading.get_ident())
525519
ex = concurrent.futures.ThreadPoolExecutor(1)
526520
f1 = ex.submit(run, 'oi')
527-
with self.assertWarns(DeprecationWarning) as cm:
528-
f2 = asyncio.wrap_future(f1)
529-
self.assertEqual(cm.warnings[0].filename, __file__)
521+
f2 = asyncio.wrap_future(f1)
530522
self.assertIs(self.loop, f2._loop)
531523
ex.shutdown(wait=True)
532524

Lib/test/test_asyncio/test_streams.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -747,10 +747,8 @@ def test_read_all_from_pipe_reader(self):
747747
self.assertEqual(data, b'data')
748748

749749
def test_streamreader_constructor_without_loop(self):
750-
with self.assertWarns(DeprecationWarning) as cm:
751-
with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'):
752-
asyncio.StreamReader()
753-
self.assertEqual(cm.warnings[0].filename, __file__)
750+
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
751+
asyncio.StreamReader()
754752

755753
def test_streamreader_constructor_use_running_loop(self):
756754
# asyncio issue #184: Ensure that StreamReaderProtocol constructor
@@ -764,21 +762,17 @@ async def test():
764762
def test_streamreader_constructor_use_global_loop(self):
765763
# asyncio issue #184: Ensure that StreamReaderProtocol constructor
766764
# retrieves the current loop if the loop parameter is not set
767-
# Deprecated in 3.10
765+
# Deprecated in 3.10, undeprecated in 3.11.1
768766
self.addCleanup(asyncio.set_event_loop, None)
769767
asyncio.set_event_loop(self.loop)
770-
with self.assertWarns(DeprecationWarning) as cm:
771-
reader = asyncio.StreamReader()
772-
self.assertEqual(cm.warnings[0].filename, __file__)
768+
reader = asyncio.StreamReader()
773769
self.assertIs(reader._loop, self.loop)
774770

775771

776772
def test_streamreaderprotocol_constructor_without_loop(self):
777773
reader = mock.Mock()
778-
with self.assertWarns(DeprecationWarning) as cm:
779-
with self.assertRaisesRegex(RuntimeError, 'There is no current event loop'):
780-
asyncio.StreamReaderProtocol(reader)
781-
self.assertEqual(cm.warnings[0].filename, __file__)
774+
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
775+
asyncio.StreamReaderProtocol(reader)
782776

783777
def test_streamreaderprotocol_constructor_use_running_loop(self):
784778
# asyncio issue #184: Ensure that StreamReaderProtocol constructor
@@ -792,13 +786,11 @@ async def test():
792786
def test_streamreaderprotocol_constructor_use_global_loop(self):
793787
# asyncio issue #184: Ensure that StreamReaderProtocol constructor
794788
# retrieves the current loop if the loop parameter is not set
795-
# Deprecated in 3.10
789+
# Deprecated in 3.10, undeprecated in 3.11.1
796790
self.addCleanup(asyncio.set_event_loop, None)
797791
asyncio.set_event_loop(self.loop)
798792
reader = mock.Mock()
799-
with self.assertWarns(DeprecationWarning) as cm:
800-
protocol = asyncio.StreamReaderProtocol(reader)
801-
self.assertEqual(cm.warnings[0].filename, __file__)
793+
protocol = asyncio.StreamReaderProtocol(reader)
802794
self.assertIs(protocol._loop, self.loop)
803795

804796
def test_multiple_drain(self):

0 commit comments

Comments
 (0)