Partially revert changes made in GH-93453.
asyncio.DefaultEventLoopPolicy.get_event_loop() now emits a
DeprecationWarning and creates and sets a new event loop instead of
raising a RuntimeError if there is no current event loop set.
Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>
running event loop.
If there is no running event loop set, the function will return
- the result of calling ``get_event_loop_policy().get_event_loop()``.
+ the result of the ``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
instead of using these lower level functions to manually create and close an
event loop.
- .. note::
- In Python versions 3.10.0--3.10.8 and 3.11.0 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.
+ .. deprecated:: 3.12
+ Deprecation warning is emitted if there is no current event loop.
+ In some future Python release this will become an error.
.. function:: set_event_loop(loop)
On Windows, :class:`ProactorEventLoop` is now used by default.
- .. versionchanged:: 3.12
- :meth:`get_event_loop` now raises a :exc:`RuntimeError` if there is no
- current event loop set.
+ .. deprecated:: 3.12
+ The :meth:`get_event_loop` method of the default asyncio policy now emits
+ a :exc:`DeprecationWarning` if there is no current event loop set and it
+ decides to create one.
+ In some future Python release this will become an error.
.. class:: WindowsSelectorEventLoopPolicy
scheduled for removal in Python 3.12.
(Contributed by Erlend E. Aasland in :issue:`42264`.)
-* :func:`asyncio.get_event_loop` now emits a deprecation warning if there is
- no running event loop. In the future it will be an alias of
- :func:`~asyncio.get_running_loop`.
- :mod:`asyncio` functions which implicitly create :class:`~asyncio.Future`
- or :class:`~asyncio.Task` objects now emit
- a deprecation warning if there is no running event loop and no explicit
- *loop* argument is passed: :func:`~asyncio.ensure_future`,
- :func:`~asyncio.wrap_future`, :func:`~asyncio.gather`,
- :func:`~asyncio.shield`, :func:`~asyncio.as_completed` and constructors of
- :class:`~asyncio.Future`, :class:`~asyncio.Task`,
- :class:`~asyncio.StreamReader`, :class:`~asyncio.StreamReaderProtocol`.
- (Contributed by Serhiy Storchaka in :issue:`39529`.)
-
* The undocumented built-in function ``sqlite3.enable_shared_cache`` is now
deprecated, scheduled for removal in Python 3.12. Its use is strongly
discouraged by the SQLite3 documentation. See `the SQLite3 docs
:exc:`ImportWarning`).
(Contributed by Brett Cannon in :gh:`65961`.)
+* The :meth:`~asyncio.DefaultEventLoopPolicy.get_event_loop` method of the
+ default event loop policy now emits a :exc:`DeprecationWarning` if there
+ is no current event loop set and it decides to create one.
+ (Contributed by Serhiy Storchaka and Guido van Rossum in :gh:`100160`.)
+
Pending Removal in Python 3.13
------------------------------
around process-global resources, which are best managed from the main interpreter.
(Contributed by Dong-hee Na in :gh:`99127`.)
-* :func:`asyncio.get_event_loop` and many other :mod:`asyncio` functions like
- :func:`~asyncio.ensure_future`, :func:`~asyncio.shield` or
- :func:`~asyncio.gather`, and also the
- :meth:`~asyncio.BaseDefaultEventLoopPolicy.get_event_loop` method of
- :class:`~asyncio.BaseDefaultEventLoopPolicy` now raise a :exc:`RuntimeError`
- if called when there is no running event loop and the current event loop was
- not set.
- Previously they implicitly created and set a new current event loop.
- :exc:`DeprecationWarning` is no longer emitted if there is no running
- event loop but the current event loop is set in the policy.
- (Contributed by Serhiy Storchaka in :gh:`93453`.)
-
Build Changes
=============
Returns an event loop object implementing the BaseEventLoop interface,
or raises an exception in case no event loop has been set for the
- current context.
+ current context and the current policy does not specify to create one.
It should never return None."""
raise NotImplementedError
Returns an instance of EventLoop or raises an exception.
"""
+ 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:
+ # Move up the call stack so that the warning is attached
+ # to the line outside asyncio itself.
+ 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:
raise RuntimeError('There is no current event loop in thread %r.'
% threading.current_thread().name)
def test_get_event_loop(self):
policy = asyncio.DefaultEventLoopPolicy()
self.assertIsNone(policy._local._loop)
- with self.assertRaisesRegex(RuntimeError, 'no current event 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)
+ self.assertIs(loop, policy.get_event_loop())
+ loop.close()
+
+ def test_get_event_loop_calls_set_event_loop(self):
+ policy = asyncio.DefaultEventLoopPolicy()
+
+ with mock.patch.object(
+ policy, "set_event_loop",
+ wraps=policy.set_event_loop) as m_set_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
+ # the child watcher correctly)
+ m_set_event_loop.assert_called_with(loop)
+
+ loop.close()
def test_get_event_loop_after_set_none(self):
policy = asyncio.DefaultEventLoopPolicy()
loop = asyncio.new_event_loop()
self.addCleanup(loop.close)
- with self.assertRaisesRegex(RuntimeError, 'no current'):
- asyncio.get_event_loop()
+ with self.assertWarns(DeprecationWarning) as cm:
+ loop2 = asyncio.get_event_loop()
+ self.addCleanup(loop2.close)
+ self.assertEqual(cm.filename, __file__)
asyncio.set_event_loop(None)
with self.assertRaisesRegex(RuntimeError, 'no current'):
asyncio.get_event_loop()
if pid == 0:
# child
try:
- loop = asyncio.get_event_loop_policy().get_event_loop()
+ with self.assertWarns(DeprecationWarning):
+ loop = asyncio.get_event_loop_policy().get_event_loop()
+ os.write(w, b'LOOP:' + str(id(loop)).encode())
except RuntimeError:
os.write(w, b'NO LOOP')
except:
os._exit(0)
else:
# parent
- self.assertEqual(os.read(r, 100), b'NO LOOP')
+ result = os.read(r, 100)
+ self.assertEqual(result[:5], b'LOOP:', result)
+ self.assertNotEqual(int(result[5:]), id(loop))
wait_process(pid, exitcode=0)
@hashlib_helper.requires_hashdigest('md5')
--- /dev/null
+Emit a deprecation warning in
+:meth:`asyncio.DefaultEventLoopPolicy.get_event_loop` if there is no current
+event loop set and it decides to create one.