Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 172c0f2

Browse filesBrowse files
bpo-39529: Deprecate creating new event loop in asyncio.get_event_loop() (GH-23554)
asyncio.get_event_loop() emits now a deprecation warning when it creates a new event loop. In future releases it will became an alias of asyncio.get_running_loop().
1 parent face87c commit 172c0f2
Copy full SHA for 172c0f2

16 files changed

+568
-183
lines changed

‎Doc/library/asyncio-eventloop.rst

Copy file name to clipboardExpand all lines: Doc/library/asyncio-eventloop.rst
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ an event loop:
5353
Consider also using the :func:`asyncio.run` function instead of using
5454
lower level functions to manually create and close an event loop.
5555

56+
.. deprecated:: 3.10
57+
Deprecation warning is emitted if there is no running event loop.
58+
If future Python releases this function will be an alias of
59+
:func:`get_running_loop`.
60+
5661
.. function:: set_event_loop(loop)
5762

5863
Set *loop* as a current event loop for the current OS thread.

‎Doc/library/asyncio-future.rst

Copy file name to clipboardExpand all lines: Doc/library/asyncio-future.rst
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,20 @@ Future Functions
5757
.. versionchanged:: 3.5.1
5858
The function accepts any :term:`awaitable` object.
5959

60+
.. deprecated:: 3.10
61+
Deprecation warning is emitted if *obj* is not a Future-like object
62+
and *loop* is not specified and there is no running event loop.
63+
6064

6165
.. function:: wrap_future(future, *, loop=None)
6266

6367
Wrap a :class:`concurrent.futures.Future` object in a
6468
:class:`asyncio.Future` object.
6569

70+
.. deprecated:: 3.10
71+
Deprecation warning is emitted if *future* is not a Future-like object
72+
and *loop* is not specified and there is no running event loop.
73+
6674

6775
Future Object
6876
=============
@@ -90,6 +98,10 @@ Future Object
9098
.. versionchanged:: 3.7
9199
Added support for the :mod:`contextvars` module.
92100

101+
.. deprecated:: 3.10
102+
Deprecation warning is emitted if *loop* is not specified
103+
and there is no running event loop.
104+
93105
.. method:: result()
94106

95107
Return the result of the Future.

‎Doc/library/asyncio-task.rst

Copy file name to clipboardExpand all lines: Doc/library/asyncio-task.rst
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,11 @@ Running Tasks Concurrently
397397
If the *gather* itself is cancelled, the cancellation is
398398
propagated regardless of *return_exceptions*.
399399

400+
.. deprecated:: 3.10
401+
Deprecation warning is emitted if no positional arguments are provided
402+
or not all positional arguments are Future-like objects
403+
and there is no running event loop.
404+
400405

401406
Shielding From Cancellation
402407
===========================
@@ -434,6 +439,10 @@ Shielding From Cancellation
434439
except CancelledError:
435440
res = None
436441

442+
.. deprecated:: 3.10
443+
Deprecation warning is emitted if *aw* is not Future-like object
444+
and there is no running event loop.
445+
437446

438447
Timeouts
439448
========
@@ -593,6 +602,10 @@ Waiting Primitives
593602
earliest_result = await coro
594603
# ...
595604

605+
.. deprecated:: 3.10
606+
Deprecation warning is emitted if not all awaitable objects in the *aws*
607+
iterable are Future-like objects and there is no running event loop.
608+
596609

597610
Running in Threads
598611
==================
@@ -775,6 +788,10 @@ Task Object
775788
.. deprecated-removed:: 3.8 3.10
776789
The *loop* parameter.
777790

791+
.. deprecated:: 3.10
792+
Deprecation warning is emitted if *loop* is not specified
793+
and there is no running event loop.
794+
778795
.. method:: cancel(msg=None)
779796

780797
Request the Task to be cancelled.

‎Doc/whatsnew/3.10.rst

Copy file name to clipboardExpand all lines: Doc/whatsnew/3.10.rst
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,19 @@ Deprecated
13491349
scheduled for removal in Python 3.12.
13501350
(Contributed by Erlend E. Aasland in :issue:`42264`.)
13511351
1352+
* :func:`asyncio.get_event_loop` emits now a deprecation warning if there is
1353+
no running event loop. In future it will be an alias of
1354+
:func:`~asyncio.get_running_loop`.
1355+
:mod:`asyncio` functions which implicitly create a :class:`~asyncio.Future`
1356+
or :class:`~asyncio.Task` objects emit now
1357+
a deprecation warning if there is no running event loop and no explicit
1358+
*loop* argument is passed: :func:`~asyncio.ensure_future`,
1359+
:func:`~asyncio.wrap_future`, :func:`~asyncio.gather`,
1360+
:func:`~asyncio.shield`, :func:`~asyncio.as_completed` and constructors of
1361+
:class:`~asyncio.Future`, :class:`~asyncio.Task`,
1362+
:class:`~asyncio.StreamReader`, :class:`~asyncio.StreamReaderProtocol`.
1363+
(Contributed by Serhiy Storchaka in :issue:`39529`.)
1364+
13521365
* The undocumented built-in function ``sqlite3.enable_shared_cache`` is now
13531366
deprecated, scheduled for removal in Python 3.12. Its use is strongly
13541367
discouraged by the SQLite3 documentation. See `the SQLite3 docs

‎Lib/asyncio/events.py

Copy file name to clipboardExpand all lines: Lib/asyncio/events.py
+10-1Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,9 +759,16 @@ def get_event_loop():
759759
the result of `get_event_loop_policy().get_event_loop()` call.
760760
"""
761761
# NOTE: this function is implemented in C (see _asynciomodule.c)
762+
return _py__get_event_loop()
763+
764+
765+
def _get_event_loop(stacklevel=3):
762766
current_loop = _get_running_loop()
763767
if current_loop is not None:
764768
return current_loop
769+
import warnings
770+
warnings.warn('There is no current event loop',
771+
DeprecationWarning, stacklevel=stacklevel)
765772
return get_event_loop_policy().get_event_loop()
766773

767774

@@ -791,14 +798,15 @@ def set_child_watcher(watcher):
791798
_py__set_running_loop = _set_running_loop
792799
_py_get_running_loop = get_running_loop
793800
_py_get_event_loop = get_event_loop
801+
_py__get_event_loop = _get_event_loop
794802

795803

796804
try:
797805
# get_event_loop() is one of the most frequently called
798806
# functions in asyncio. Pure Python implementation is
799807
# about 4 times slower than C-accelerated.
800808
from _asyncio import (_get_running_loop, _set_running_loop,
801-
get_running_loop, get_event_loop)
809+
get_running_loop, get_event_loop, _get_event_loop)
802810
except ImportError:
803811
pass
804812
else:
@@ -807,3 +815,4 @@ def set_child_watcher(watcher):
807815
_c__set_running_loop = _set_running_loop
808816
_c_get_running_loop = get_running_loop
809817
_c_get_event_loop = get_event_loop
818+
_c__get_event_loop = _get_event_loop

‎Lib/asyncio/futures.py

Copy file name to clipboardExpand all lines: Lib/asyncio/futures.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def __init__(self, *, loop=None):
7676
the default event loop.
7777
"""
7878
if loop is None:
79-
self._loop = events.get_event_loop()
79+
self._loop = events._get_event_loop()
8080
else:
8181
self._loop = loop
8282
self._callbacks = []
@@ -408,7 +408,7 @@ def wrap_future(future, *, loop=None):
408408
assert isinstance(future, concurrent.futures.Future), \
409409
f'concurrent.futures.Future is expected, got {future!r}'
410410
if loop is None:
411-
loop = events.get_event_loop()
411+
loop = events._get_event_loop()
412412
new_future = loop.create_future()
413413
_chain_future(future, new_future)
414414
return new_future

‎Lib/asyncio/streams.py

Copy file name to clipboardExpand all lines: Lib/asyncio/streams.py
+9-5Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class FlowControlMixin(protocols.Protocol):
125125

126126
def __init__(self, loop=None):
127127
if loop is None:
128-
self._loop = events.get_event_loop()
128+
self._loop = events._get_event_loop(stacklevel=4)
129129
else:
130130
self._loop = loop
131131
self._paused = False
@@ -283,9 +283,13 @@ def _get_close_waiter(self, stream):
283283
def __del__(self):
284284
# Prevent reports about unhandled exceptions.
285285
# Better than self._closed._log_traceback = False hack
286-
closed = self._closed
287-
if closed.done() and not closed.cancelled():
288-
closed.exception()
286+
try:
287+
closed = self._closed
288+
except AttributeError:
289+
pass # failed constructor
290+
else:
291+
if closed.done() and not closed.cancelled():
292+
closed.exception()
289293

290294

291295
class StreamWriter:
@@ -381,7 +385,7 @@ def __init__(self, limit=_DEFAULT_LIMIT, loop=None):
381385

382386
self._limit = limit
383387
if loop is None:
384-
self._loop = events.get_event_loop()
388+
self._loop = events._get_event_loop()
385389
else:
386390
self._loop = loop
387391
self._buffer = bytearray()

‎Lib/asyncio/tasks.py

Copy file name to clipboardExpand all lines: Lib/asyncio/tasks.py
+23-19Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ def as_completed(fs, *, timeout=None):
549549
from .queues import Queue # Import here to avoid circular import problem.
550550
done = Queue()
551551

552-
loop = events.get_event_loop()
552+
loop = events._get_event_loop()
553553
todo = {ensure_future(f, loop=loop) for f in set(fs)}
554554
timeout_handle = None
555555

@@ -616,23 +616,26 @@ def ensure_future(coro_or_future, *, loop=None):
616616
617617
If the argument is a Future, it is returned directly.
618618
"""
619-
if coroutines.iscoroutine(coro_or_future):
620-
if loop is None:
621-
loop = events.get_event_loop()
622-
task = loop.create_task(coro_or_future)
623-
if task._source_traceback:
624-
del task._source_traceback[-1]
625-
return task
626-
elif futures.isfuture(coro_or_future):
619+
return _ensure_future(coro_or_future, loop=loop)
620+
621+
622+
def _ensure_future(coro_or_future, *, loop=None):
623+
if futures.isfuture(coro_or_future):
627624
if loop is not None and loop is not futures._get_loop(coro_or_future):
628625
raise ValueError('The future belongs to a different loop than '
629-
'the one specified as the loop argument')
626+
'the one specified as the loop argument')
630627
return coro_or_future
631-
elif inspect.isawaitable(coro_or_future):
632-
return ensure_future(_wrap_awaitable(coro_or_future), loop=loop)
633-
else:
634-
raise TypeError('An asyncio.Future, a coroutine or an awaitable is '
635-
'required')
628+
629+
if not coroutines.iscoroutine(coro_or_future):
630+
if inspect.isawaitable(coro_or_future):
631+
coro_or_future = _wrap_awaitable(coro_or_future)
632+
else:
633+
raise TypeError('An asyncio.Future, a coroutine or an awaitable '
634+
'is required')
635+
636+
if loop is None:
637+
loop = events._get_event_loop(stacklevel=4)
638+
return loop.create_task(coro_or_future)
636639

637640

638641
@types.coroutine
@@ -655,7 +658,8 @@ class _GatheringFuture(futures.Future):
655658
cancelled.
656659
"""
657660

658-
def __init__(self, children, *, loop=None):
661+
def __init__(self, children, *, loop):
662+
assert loop is not None
659663
super().__init__(loop=loop)
660664
self._children = children
661665
self._cancel_requested = False
@@ -706,7 +710,7 @@ def gather(*coros_or_futures, return_exceptions=False):
706710
gather won't cancel any other awaitables.
707711
"""
708712
if not coros_or_futures:
709-
loop = events.get_event_loop()
713+
loop = events._get_event_loop()
710714
outer = loop.create_future()
711715
outer.set_result([])
712716
return outer
@@ -773,7 +777,7 @@ def _done_callback(fut):
773777
loop = None
774778
for arg in coros_or_futures:
775779
if arg not in arg_to_fut:
776-
fut = ensure_future(arg, loop=loop)
780+
fut = _ensure_future(arg, loop=loop)
777781
if loop is None:
778782
loop = futures._get_loop(fut)
779783
if fut is not arg:
@@ -823,7 +827,7 @@ def shield(arg):
823827
except CancelledError:
824828
res = None
825829
"""
826-
inner = ensure_future(arg)
830+
inner = _ensure_future(arg)
827831
if inner.done():
828832
# Shortcut.
829833
return inner

‎Lib/test/test_asyncio/test_events.py

Copy file name to clipboardExpand all lines: Lib/test/test_asyncio/test_events.py
+67-10Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2702,14 +2702,18 @@ def get_event_loop(self):
27022702
asyncio.set_event_loop_policy(Policy())
27032703
loop = asyncio.new_event_loop()
27042704

2705-
with self.assertRaises(TestError):
2706-
asyncio.get_event_loop()
2705+
with self.assertWarns(DeprecationWarning) as cm:
2706+
with self.assertRaises(TestError):
2707+
asyncio.get_event_loop()
2708+
self.assertEqual(cm.warnings[0].filename, __file__)
27072709
asyncio.set_event_loop(None)
2708-
with self.assertRaises(TestError):
2709-
asyncio.get_event_loop()
2710+
with self.assertWarns(DeprecationWarning) as cm:
2711+
with self.assertRaises(TestError):
2712+
asyncio.get_event_loop()
2713+
self.assertEqual(cm.warnings[0].filename, __file__)
27102714

27112715
with self.assertRaisesRegex(RuntimeError, 'no running'):
2712-
self.assertIs(asyncio.get_running_loop(), None)
2716+
asyncio.get_running_loop()
27132717
self.assertIs(asyncio._get_running_loop(), None)
27142718

27152719
async def func():
@@ -2720,20 +2724,73 @@ async def func():
27202724
loop.run_until_complete(func())
27212725

27222726
asyncio.set_event_loop(loop)
2723-
with self.assertRaises(TestError):
2724-
asyncio.get_event_loop()
2727+
with self.assertWarns(DeprecationWarning) as cm:
2728+
with self.assertRaises(TestError):
2729+
asyncio.get_event_loop()
2730+
self.assertEqual(cm.warnings[0].filename, __file__)
27252731

27262732
asyncio.set_event_loop(None)
2727-
with self.assertRaises(TestError):
2728-
asyncio.get_event_loop()
2733+
with self.assertWarns(DeprecationWarning) as cm:
2734+
with self.assertRaises(TestError):
2735+
asyncio.get_event_loop()
2736+
self.assertEqual(cm.warnings[0].filename, __file__)
27292737

27302738
finally:
27312739
asyncio.set_event_loop_policy(old_policy)
27322740
if loop is not None:
27332741
loop.close()
27342742

27352743
with self.assertRaisesRegex(RuntimeError, 'no running'):
2736-
self.assertIs(asyncio.get_running_loop(), None)
2744+
asyncio.get_running_loop()
2745+
2746+
self.assertIs(asyncio._get_running_loop(), None)
2747+
2748+
def test_get_event_loop_returns_running_loop2(self):
2749+
old_policy = asyncio.get_event_loop_policy()
2750+
try:
2751+
asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
2752+
loop = asyncio.new_event_loop()
2753+
self.addCleanup(loop.close)
2754+
2755+
with self.assertWarns(DeprecationWarning) as cm:
2756+
loop2 = asyncio.get_event_loop()
2757+
self.addCleanup(loop2.close)
2758+
self.assertEqual(cm.warnings[0].filename, __file__)
2759+
asyncio.set_event_loop(None)
2760+
with self.assertWarns(DeprecationWarning) as cm:
2761+
with self.assertRaisesRegex(RuntimeError, 'no current'):
2762+
asyncio.get_event_loop()
2763+
self.assertEqual(cm.warnings[0].filename, __file__)
2764+
2765+
with self.assertRaisesRegex(RuntimeError, 'no running'):
2766+
asyncio.get_running_loop()
2767+
self.assertIs(asyncio._get_running_loop(), None)
2768+
2769+
async def func():
2770+
self.assertIs(asyncio.get_event_loop(), loop)
2771+
self.assertIs(asyncio.get_running_loop(), loop)
2772+
self.assertIs(asyncio._get_running_loop(), loop)
2773+
2774+
loop.run_until_complete(func())
2775+
2776+
asyncio.set_event_loop(loop)
2777+
with self.assertWarns(DeprecationWarning) as cm:
2778+
self.assertIs(asyncio.get_event_loop(), loop)
2779+
self.assertEqual(cm.warnings[0].filename, __file__)
2780+
2781+
asyncio.set_event_loop(None)
2782+
with self.assertWarns(DeprecationWarning) as cm:
2783+
with self.assertRaisesRegex(RuntimeError, 'no current'):
2784+
asyncio.get_event_loop()
2785+
self.assertEqual(cm.warnings[0].filename, __file__)
2786+
2787+
finally:
2788+
asyncio.set_event_loop_policy(old_policy)
2789+
if loop is not None:
2790+
loop.close()
2791+
2792+
with self.assertRaisesRegex(RuntimeError, 'no running'):
2793+
asyncio.get_running_loop()
27372794

27382795
self.assertIs(asyncio._get_running_loop(), None)
27392796

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.