]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-126353: remove implicit creation of loop from `asyncio.get_event_loop` (#126354)
authorKumar Aditya <kumaraditya@python.org>
Mon, 4 Nov 2024 08:51:20 +0000 (14:21 +0530)
committerGitHub <noreply@github.com>
Mon, 4 Nov 2024 08:51:20 +0000 (14:21 +0530)
Remove implicit creation of loop from `asyncio.get_event_loop`. This is a step forward of deprecating the policy system of asyncio.

Doc/library/asyncio-eventloop.rst
Doc/library/asyncio-policy.rst
Doc/whatsnew/3.14.rst
Lib/asyncio/events.py
Lib/test/test_asyncio/test_events.py
Lib/test/test_asyncio/test_unix_events.py
Lib/test/test_cmd_line.py
Misc/NEWS.d/next/Library/2024-11-03-10-48-07.gh-issue-126353.ChDzot.rst [new file with mode: 0644]

index 14fd153f640f05de75656abf958aacb6407f253a..3ace6eda4d7f291fadc3be4be9a5bf1062c02097 100644 (file)
@@ -59,9 +59,8 @@ an event loop:
    instead of using these lower level functions to manually create and close an
    event loop.
 
-   .. deprecated:: 3.12
-      Deprecation warning is emitted if there is no current event loop.
-      In some future Python release this will become an error.
+   .. versionchanged:: 3.14
+      Raises a :exc:`RuntimeError` if there is no current event loop.
 
 .. function:: set_event_loop(loop)
 
index 837ccc6606786ede5ae9d4d2939b02b67396a507..09b75762ff0272c29a47796348267fe3a54dd793 100644 (file)
@@ -97,11 +97,9 @@ asyncio ships with the following built-in policies:
 
       On Windows, :class:`ProactorEventLoop` is now used by default.
 
-   .. 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.
+   .. versionchanged:: 3.14
+      The :meth:`get_event_loop` method of the default asyncio policy now
+      raises a :exc:`RuntimeError` if there is no set event loop.
 
 
 .. class:: WindowsSelectorEventLoopPolicy
index 21bc289c2be5d8113f860960af704843ffd25716..deee683d7b87b1cf942cef1cb0d6f578b15eda1b 100644 (file)
@@ -576,6 +576,11 @@ asyncio
 
   (Contributed by Kumar Aditya in :gh:`120804`.)
 
+* Removed implicit creation of event loop by :func:`asyncio.get_event_loop`.
+  It now raises a :exc:`RuntimeError` if there is no current event loop.
+  (Contributed by Kumar Aditya in :gh:`126353`.)
+
+
 collections.abc
 ---------------
 
index b63fe6aa79604b71f354785b9e8c1f6ef7a3b912..ca0a4f2fee5840f1f0ab848d82e9ca49b465a99a 100644 (file)
@@ -668,7 +668,6 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
 
     class _Local(threading.local):
         _loop = None
-        _set_called = False
 
     def __init__(self):
         self._local = self._Local()
@@ -678,28 +677,6 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
 
         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)
@@ -708,7 +685,6 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
 
     def set_event_loop(self, loop):
         """Set the event loop."""
-        self._local._set_called = True
         if loop is not None and not isinstance(loop, AbstractEventLoop):
             raise TypeError(f"loop must be an instance of AbstractEventLoop or None, not '{type(loop).__name__}'")
         self._local._loop = loop
index 4dcf9f0e4037b621d83c1b70d961d6b4dba74fdd..2ab638dc527aec0713fb114137f12091afbe8f9b 100644 (file)
@@ -2704,33 +2704,22 @@ class PolicyTests(unittest.TestCase):
     def test_get_event_loop(self):
         policy = asyncio.DefaultEventLoopPolicy()
         self.assertIsNone(policy._local._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()
+        with self.assertRaises(RuntimeError):
+            loop = policy.get_event_loop()
+        self.assertIsNone(policy._local._loop)
 
-    def test_get_event_loop_calls_set_event_loop(self):
+    def test_get_event_loop_does_not_call_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:
+            with self.assertRaises(RuntimeError):
                 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()
+            m_set_event_loop.assert_not_called()
 
     def test_get_event_loop_after_set_none(self):
         policy = asyncio.DefaultEventLoopPolicy()
@@ -2912,17 +2901,12 @@ class GetEventLoopTestsMixin:
             loop = asyncio.new_event_loop()
             self.addCleanup(loop.close)
 
-            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()
 
-            with self.assertRaisesRegex(RuntimeError, 'no running'):
-                asyncio.get_running_loop()
-            self.assertIs(asyncio._get_running_loop(), None)
+            asyncio.set_event_loop(None)
+            with self.assertRaisesRegex(RuntimeError, 'no current'):
+                asyncio.get_event_loop()
 
             async def func():
                 self.assertIs(asyncio.get_event_loop(), loop)
index 9ae54b6887010b260a2ee0e9ed4eba337da38a59..021f45478d6f4844c12fe140d0fb914bed598ca1 100644 (file)
@@ -1195,8 +1195,7 @@ class TestFork(unittest.IsolatedAsyncioTestCase):
         if pid == 0:
             # child
             try:
-                with self.assertWarns(DeprecationWarning):
-                    loop = asyncio.get_event_loop_policy().get_event_loop()
+                loop = asyncio.get_event_loop()
                 os.write(w, b'LOOP:' + str(id(loop)).encode())
             except RuntimeError:
                 os.write(w, b'NO LOOP')
@@ -1207,8 +1206,7 @@ class TestFork(unittest.IsolatedAsyncioTestCase):
         else:
             # parent
             result = os.read(r, 100)
-            self.assertEqual(result[:5], b'LOOP:', result)
-            self.assertNotEqual(int(result[5:]), id(loop))
+            self.assertEqual(result, b'NO LOOP')
             wait_process(pid, exitcode=0)
 
     @hashlib_helper.requires_hashdigest('md5')
index 35725718152c56802b2aeb695d1207324a22db91..eca9adf9a7dcbc5e0bf70d58da72a408a6a5bdf5 100644 (file)
@@ -924,7 +924,7 @@ class CmdLineTest(unittest.TestCase):
                 self.assertEqual(proc.stderr, '')
 
     def test_python_asyncio_debug(self):
-        code = "import asyncio; print(asyncio.get_event_loop().get_debug())"
+        code = "import asyncio; print(asyncio.new_event_loop().get_debug())"
         rc, out, err = assert_python_ok('-c', code, PYTHONASYNCIODEBUG='1')
         self.assertIn(b'True', out)
 
diff --git a/Misc/NEWS.d/next/Library/2024-11-03-10-48-07.gh-issue-126353.ChDzot.rst b/Misc/NEWS.d/next/Library/2024-11-03-10-48-07.gh-issue-126353.ChDzot.rst
new file mode 100644 (file)
index 0000000..16d508b
--- /dev/null
@@ -0,0 +1,2 @@
+:func:`asyncio.get_event_loop` now does not implicitly creates an event loop.
+It now raises a :exc:`RuntimeError` if there is no set event loop. Patch by Kumar Aditya.