]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-94597: Deprecate child watcher getters and setters (#98215)
authorKumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Sat, 15 Oct 2022 23:09:30 +0000 (04:39 +0530)
committerGitHub <noreply@github.com>
Sat, 15 Oct 2022 23:09:30 +0000 (16:09 -0700)
This is the next step for deprecating child watchers.

Until we've removed the API completely we have to use it, so this PR is mostly suppressing a lot of warnings when using the API internally.

Once the child watcher API is totally removed, the two child watcher implementations we actually use and need (Pidfd and Thread) will be turned into internal helpers.

Doc/library/asyncio-policy.rst
Doc/whatsnew/3.12.rst
Lib/asyncio/unix_events.py
Lib/test/test_asyncio/test_events.py
Lib/test/test_asyncio/test_streams.py
Lib/test/test_asyncio/test_subprocess.py
Lib/test/test_asyncio/test_unix_events.py
Lib/test/test_asyncio/utils.py
Misc/NEWS.d/next/Library/2022-10-12-11-20-54.gh-issue-94597.GYJZlb.rst [new file with mode: 0644]

index bfc3e3090fdcdf1acb525f6a40b9986a7aef871e..052378ef32743b54466c86fb7824dc957ba31132 100644 (file)
@@ -88,12 +88,16 @@ The abstract event loop policy base class is defined as follows:
 
       This function is Unix specific.
 
+      .. deprecated:: 3.12
+
    .. method:: set_child_watcher(watcher)
 
       Set the current child process watcher to *watcher*.
 
       This function is Unix specific.
 
+      .. deprecated:: 3.12
+
 
 .. _asyncio-policy-builtin:
 
@@ -158,12 +162,16 @@ implementation used by the asyncio event loop:
 
    Return the current child watcher for the current policy.
 
+   .. deprecated:: 3.12
+
 .. function:: set_child_watcher(watcher)
 
    Set the current child watcher to *watcher* for the current
    policy.  *watcher* must implement methods defined in the
    :class:`AbstractChildWatcher` base class.
 
+   .. deprecated:: 3.12
+
 .. note::
    Third-party event loops implementations might not support
    custom child watchers.  For such event loops, using
@@ -245,6 +253,8 @@ implementation used by the asyncio event loop:
 
    .. versionadded:: 3.8
 
+   .. deprecated:: 3.12
+
 .. class:: SafeChildWatcher
 
    This implementation uses active event loop from the main thread to handle
@@ -257,6 +267,8 @@ implementation used by the asyncio event loop:
    This solution is as safe as :class:`MultiLoopChildWatcher` and has the same *O(N)*
    complexity but requires a running event loop in the main thread to work.
 
+   .. deprecated:: 3.12
+
 .. class:: FastChildWatcher
 
    This implementation reaps every terminated processes by calling
@@ -269,6 +281,8 @@ implementation used by the asyncio event loop:
    This solution requires a running event loop in the main thread to work, as
    :class:`SafeChildWatcher`.
 
+   .. deprecated:: 3.12
+
 .. class:: PidfdChildWatcher
 
    This implementation polls process file descriptors (pidfds) to await child
index ebc490691e308b082e032a9b9e366de0b76dec0d..ecc74e9beba26430a6b2428dd59624c6a2115c3d 100644 (file)
@@ -126,6 +126,12 @@ asyncio
   if supported and :class:`~asyncio.ThreadedChildWatcher` otherwise).
   (Contributed by Kumar Aditya in :gh:`94597`.)
 
+* :func:`asyncio.set_child_watcher`, :func:`asyncio.get_child_watcher`,
+  :meth:`asyncio.AbstractEventLoopPolicy.set_child_watcher` and
+  :meth:`asyncio.AbstractEventLoopPolicy.get_child_watcher` are deprecated
+  and will be removed in Python 3.14.
+  (Contributed by Kumar Aditya in :gh:`94597`.)
+
 
 pathlib
 -------
index 9f9b03e0cb21d3e2041fb4aa14f7c6fa4fd3bd09..ea7010ee10732206dbaa2b410919e48b432570be 100644 (file)
@@ -195,30 +195,32 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
     async def _make_subprocess_transport(self, protocol, args, shell,
                                          stdin, stdout, stderr, bufsize,
                                          extra=None, **kwargs):
-        with events.get_child_watcher() as watcher:
-            if not watcher.is_active():
-                # Check early.
-                # Raising exception before process creation
-                # prevents subprocess execution if the watcher
-                # is not ready to handle it.
-                raise RuntimeError("asyncio.get_child_watcher() is not activated, "
-                                   "subprocess support is not installed.")
-            waiter = self.create_future()
-            transp = _UnixSubprocessTransport(self, protocol, args, shell,
-                                              stdin, stdout, stderr, bufsize,
-                                              waiter=waiter, extra=extra,
-                                              **kwargs)
-
-            watcher.add_child_handler(transp.get_pid(),
-                                      self._child_watcher_callback, transp)
-            try:
-                await waiter
-            except (SystemExit, KeyboardInterrupt):
-                raise
-            except BaseException:
-                transp.close()
-                await transp._wait()
-                raise
+        with warnings.catch_warnings():
+            warnings.simplefilter('ignore', DeprecationWarning)
+            with events.get_child_watcher() as watcher:
+                if not watcher.is_active():
+                    # Check early.
+                    # Raising exception before process creation
+                    # prevents subprocess execution if the watcher
+                    # is not ready to handle it.
+                    raise RuntimeError("asyncio.get_child_watcher() is not activated, "
+                                    "subprocess support is not installed.")
+                waiter = self.create_future()
+                transp = _UnixSubprocessTransport(self, protocol, args, shell,
+                                                stdin, stdout, stderr, bufsize,
+                                                waiter=waiter, extra=extra,
+                                                **kwargs)
+
+                watcher.add_child_handler(transp.get_pid(),
+                                        self._child_watcher_callback, transp)
+                try:
+                    await waiter
+                except (SystemExit, KeyboardInterrupt):
+                    raise
+                except BaseException:
+                    transp.close()
+                    await transp._wait()
+                    raise
 
         return transp
 
@@ -1469,6 +1471,9 @@ class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
         if self._watcher is None:
             self._init_watcher()
 
+        warnings._deprecated("get_child_watcher",
+                            "{name!r} is deprecated as of Python 3.12 and will be "
+                            "removed in Python {remove}.", remove=(3, 14))
         return self._watcher
 
     def set_child_watcher(self, watcher):
@@ -1480,6 +1485,9 @@ class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
             self._watcher.close()
 
         self._watcher = watcher
+        warnings._deprecated("set_child_watcher",
+                            "{name!r} is deprecated as of Python 3.12 and will be "
+                            "removed in Python {remove}.", remove=(3, 14))
 
 
 SelectorEventLoop = _UnixSelectorEventLoop
index 98b55dec37f478404d32790d8f7c397cb44d024b..cabe75f56d9fb0a7ef4c7dbca22b3cc4938c0979 100644 (file)
@@ -2058,11 +2058,13 @@ else:
             with warnings.catch_warnings():
                 warnings.simplefilter('ignore', DeprecationWarning)
                 watcher = asyncio.SafeChildWatcher()
-            watcher.attach_loop(self.loop)
-            asyncio.set_child_watcher(watcher)
+                watcher.attach_loop(self.loop)
+                asyncio.set_child_watcher(watcher)
 
         def tearDown(self):
-            asyncio.set_child_watcher(None)
+            with warnings.catch_warnings():
+                warnings.simplefilter('ignore', DeprecationWarning)
+                asyncio.set_child_watcher(None)
             super().tearDown()
 
 
@@ -2657,13 +2659,15 @@ class GetEventLoopTestsMixin:
             with warnings.catch_warnings():
                 warnings.simplefilter('ignore', DeprecationWarning)
                 watcher = asyncio.SafeChildWatcher()
-            watcher.attach_loop(self.loop)
-            asyncio.set_child_watcher(watcher)
+                watcher.attach_loop(self.loop)
+                asyncio.set_child_watcher(watcher)
 
     def tearDown(self):
         try:
             if sys.platform != 'win32':
-                asyncio.set_child_watcher(None)
+                with warnings.catch_warnings():
+                    warnings.simplefilter('ignore', DeprecationWarning)
+                    asyncio.set_child_watcher(None)
 
             super().tearDown()
         finally:
index 8fb9313e09dd0ea186a15b03e9979449a80bd7ab..01d5407a497a040edb798eeb172e44033178cf21 100644 (file)
@@ -797,7 +797,9 @@ os.close(fd)
             watcher = asyncio.SafeChildWatcher()
         watcher.attach_loop(self.loop)
         try:
-            asyncio.set_child_watcher(watcher)
+            with warnings.catch_warnings():
+                warnings.simplefilter('ignore', DeprecationWarning)
+                asyncio.set_child_watcher(watcher)
             create = asyncio.create_subprocess_exec(
                 *args,
                 pass_fds={wfd},
@@ -805,7 +807,9 @@ os.close(fd)
             proc = self.loop.run_until_complete(create)
             self.loop.run_until_complete(proc.wait())
         finally:
-            asyncio.set_child_watcher(None)
+            with warnings.catch_warnings():
+                warnings.simplefilter('ignore', DeprecationWarning)
+                asyncio.set_child_watcher(None)
 
         os.close(wfd)
         data = self.loop.run_until_complete(reader.read(-1))
index 64df1b17189b74f283a7d1a74884808b63862097..8e55115590617e7f6064ea398840c85bfd27a89f 100644 (file)
@@ -582,7 +582,9 @@ class SubprocessMixin:
         # manually to avoid a warning when the watcher is detached.
         if (sys.platform != 'win32' and
                 isinstance(self, SubprocessFastWatcherTests)):
-            asyncio.get_child_watcher()._callbacks.clear()
+            with warnings.catch_warnings():
+                warnings.simplefilter('ignore', DeprecationWarning)
+                asyncio.get_child_watcher()._callbacks.clear()
 
     async def _test_popen_error(self, stdin):
         if sys.platform == 'win32':
@@ -696,13 +698,17 @@ if sys.platform != 'win32':
 
             watcher = self._get_watcher()
             watcher.attach_loop(self.loop)
-            policy.set_child_watcher(watcher)
+            with warnings.catch_warnings():
+                warnings.simplefilter('ignore', DeprecationWarning)
+                policy.set_child_watcher(watcher)
 
         def tearDown(self):
             super().tearDown()
             policy = asyncio.get_event_loop_policy()
-            watcher = policy.get_child_watcher()
-            policy.set_child_watcher(None)
+            with warnings.catch_warnings():
+                warnings.simplefilter('ignore', DeprecationWarning)
+                watcher = policy.get_child_watcher()
+                policy.set_child_watcher(None)
             watcher.attach_loop(None)
             watcher.close()
 
@@ -752,7 +758,9 @@ if sys.platform != 'win32':
             )
 
             async def execute():
-                asyncio.set_child_watcher(watcher)
+                with warnings.catch_warnings():
+                    warnings.simplefilter('ignore', DeprecationWarning)
+                    asyncio.set_child_watcher(watcher)
 
                 with self.assertRaises(RuntimeError):
                     await subprocess.create_subprocess_exec(
@@ -761,7 +769,9 @@ if sys.platform != 'win32':
                 watcher.add_child_handler.assert_not_called()
 
             with asyncio.Runner(loop_factory=asyncio.new_event_loop) as runner:
-                self.assertIsNone(runner.run(execute()))
+                with warnings.catch_warnings():
+                    warnings.simplefilter('ignore', DeprecationWarning)
+                    self.assertIsNone(runner.run(execute()))
             self.assertListEqual(watcher.mock_calls, [
                 mock.call.__enter__(),
                 mock.call.__enter__().is_active(),
@@ -788,15 +798,16 @@ if sys.platform != 'win32':
                 with self.assertRaises(RuntimeError):
                     asyncio.get_event_loop_policy().get_event_loop()
                 return await asyncio.to_thread(asyncio.run, in_thread())
-
-            asyncio.set_child_watcher(asyncio.PidfdChildWatcher())
+            with self.assertWarns(DeprecationWarning):
+                asyncio.set_child_watcher(asyncio.PidfdChildWatcher())
             try:
                 with asyncio.Runner(loop_factory=asyncio.new_event_loop) as runner:
                     returncode, stdout = runner.run(main())
                 self.assertEqual(returncode, 0)
                 self.assertEqual(stdout, b'some data')
             finally:
-                asyncio.set_child_watcher(None)
+                with self.assertWarns(DeprecationWarning):
+                    asyncio.set_child_watcher(None)
 else:
     # Windows
     class SubprocessProactorTests(SubprocessMixin, test_utils.TestCase):
index 025da0f20ed47e3fadc40d7bdc69412fc9294a6a..d806ed497aaab455ded1ad3dd099c7e28a554329 100644 (file)
@@ -1709,33 +1709,36 @@ class PolicyTests(unittest.TestCase):
         self.assertIsNone(policy._watcher)
         unix_events.can_use_pidfd = mock.Mock()
         unix_events.can_use_pidfd.return_value = False
-        watcher = policy.get_child_watcher()
+        with self.assertWarns(DeprecationWarning):
+            watcher = policy.get_child_watcher()
         self.assertIsInstance(watcher, asyncio.ThreadedChildWatcher)
 
         self.assertIs(policy._watcher, watcher)
-
-        self.assertIs(watcher, policy.get_child_watcher())
+        with self.assertWarns(DeprecationWarning):
+            self.assertIs(watcher, policy.get_child_watcher())
 
         policy = self.create_policy()
         self.assertIsNone(policy._watcher)
         unix_events.can_use_pidfd = mock.Mock()
         unix_events.can_use_pidfd.return_value = True
-        watcher = policy.get_child_watcher()
+        with self.assertWarns(DeprecationWarning):
+            watcher = policy.get_child_watcher()
         self.assertIsInstance(watcher, asyncio.PidfdChildWatcher)
 
         self.assertIs(policy._watcher, watcher)
-
-        self.assertIs(watcher, policy.get_child_watcher())
+        with self.assertWarns(DeprecationWarning):
+            self.assertIs(watcher, policy.get_child_watcher())
 
     def test_get_child_watcher_after_set(self):
         policy = self.create_policy()
         with warnings.catch_warnings():
             warnings.simplefilter("ignore", DeprecationWarning)
             watcher = asyncio.FastChildWatcher()
+            policy.set_child_watcher(watcher)
 
-        policy.set_child_watcher(watcher)
         self.assertIs(policy._watcher, watcher)
-        self.assertIs(watcher, policy.get_child_watcher())
+        with self.assertWarns(DeprecationWarning):
+            self.assertIs(watcher, policy.get_child_watcher())
 
     def test_get_child_watcher_thread(self):
 
@@ -1769,7 +1772,7 @@ class PolicyTests(unittest.TestCase):
         with warnings.catch_warnings():
             warnings.simplefilter("ignore", DeprecationWarning)
             watcher = asyncio.SafeChildWatcher()
-        policy.set_child_watcher(watcher)
+            policy.set_child_watcher(watcher)
         watcher.attach_loop(loop)
 
         self.assertIs(watcher._loop, loop)
index 96be5a1c3bcf77d4dad49be639dc443c31e6e047..5b9c86eb9859a0d27bfe3a93f58389f59d868239 100644 (file)
@@ -14,7 +14,7 @@ import sys
 import threading
 import unittest
 import weakref
-
+import warnings
 from unittest import mock
 
 from http.server import HTTPServer
@@ -544,7 +544,9 @@ class TestCase(unittest.TestCase):
         policy = support.maybe_get_event_loop_policy()
         if policy is not None:
             try:
-                watcher = policy.get_child_watcher()
+                with warnings.catch_warnings():
+                    warnings.simplefilter('ignore', DeprecationWarning)
+                    watcher = policy.get_child_watcher()
             except NotImplementedError:
                 # watcher is not implemented by EventLoopPolicy, e.g. Windows
                 pass
diff --git a/Misc/NEWS.d/next/Library/2022-10-12-11-20-54.gh-issue-94597.GYJZlb.rst b/Misc/NEWS.d/next/Library/2022-10-12-11-20-54.gh-issue-94597.GYJZlb.rst
new file mode 100644 (file)
index 0000000..5ea1358
--- /dev/null
@@ -0,0 +1 @@
+Deprecated :meth:`asyncio.AbstractEventLoopPolicy.get_child_watcher` and :meth:`asyncio.AbstractEventLoopPolicy.set_child_watcher` methods to be removed in Python 3.14. Patch by Kumar Aditya.