]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-120804: Remove `get_child_watcher` and `set_child_watcher` from asyncio (#120818)
authorKumar Aditya <kumaraditya@python.org>
Sun, 23 Jun 2024 04:23:23 +0000 (09:53 +0530)
committerGitHub <noreply@github.com>
Sun, 23 Jun 2024 04:23:23 +0000 (09:53 +0530)
Lib/asyncio/events.py
Lib/asyncio/unix_events.py
Lib/test/test_asyncio/test_events.py
Lib/test/test_asyncio/test_subprocess.py
Lib/test/test_asyncio/test_unix_events.py
Lib/test/test_asyncio/utils.py

index be495469a0558b9c6fcfd4c55ce91efc95c00deb..b63fe6aa79604b71f354785b9e8c1f6ef7a3b912 100644 (file)
@@ -10,7 +10,6 @@ __all__ = (
     'Handle', 'TimerHandle',
     'get_event_loop_policy', 'set_event_loop_policy',
     'get_event_loop', 'set_event_loop', 'new_event_loop',
-    'get_child_watcher', 'set_child_watcher',
     '_set_running_loop', 'get_running_loop',
     '_get_running_loop',
 )
@@ -652,17 +651,6 @@ class AbstractEventLoopPolicy:
         the current context, set_event_loop must be called explicitly."""
         raise NotImplementedError
 
-    # Child processes handling (Unix only).
-
-    def get_child_watcher(self):
-        "Get the watcher for child processes."
-        raise NotImplementedError
-
-    def set_child_watcher(self, watcher):
-        """Set the watcher for child processes."""
-        raise NotImplementedError
-
-
 class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
     """Default policy implementation for accessing the event loop.
 
@@ -837,17 +825,6 @@ def new_event_loop():
     return get_event_loop_policy().new_event_loop()
 
 
-def get_child_watcher():
-    """Equivalent to calling get_event_loop_policy().get_child_watcher()."""
-    return get_event_loop_policy().get_child_watcher()
-
-
-def set_child_watcher(watcher):
-    """Equivalent to calling
-    get_event_loop_policy().set_child_watcher(watcher)."""
-    return get_event_loop_policy().set_child_watcher(watcher)
-
-
 # Alias pure-Python implementations for testing purposes.
 _py__get_running_loop = _get_running_loop
 _py__set_running_loop = _set_running_loop
index 9a2e300259ee8c9ccffef16915ef63ae9318bbbf..ff2df653e41b89ce97a1f45c1a820464a41dfb90 100644 (file)
@@ -199,7 +199,7 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
                                          extra=None, **kwargs):
         with warnings.catch_warnings():
             warnings.simplefilter('ignore', DeprecationWarning)
-            watcher = events.get_child_watcher()
+            watcher = events.get_event_loop_policy()._watcher
 
         with watcher:
             if not watcher.is_active():
@@ -1009,59 +1009,6 @@ class PidfdChildWatcher(AbstractChildWatcher):
         return True
 
 
-class BaseChildWatcher(AbstractChildWatcher):
-
-    def __init__(self):
-        self._loop = None
-        self._callbacks = {}
-
-    def close(self):
-        self.attach_loop(None)
-
-    def is_active(self):
-        return self._loop is not None and self._loop.is_running()
-
-    def _do_waitpid(self, expected_pid):
-        raise NotImplementedError()
-
-    def _do_waitpid_all(self):
-        raise NotImplementedError()
-
-    def attach_loop(self, loop):
-        assert loop is None or isinstance(loop, events.AbstractEventLoop)
-
-        if self._loop is not None and loop is None and self._callbacks:
-            warnings.warn(
-                'A loop is being detached '
-                'from a child watcher with pending handlers',
-                RuntimeWarning)
-
-        if self._loop is not None:
-            self._loop.remove_signal_handler(signal.SIGCHLD)
-
-        self._loop = loop
-        if loop is not None:
-            loop.add_signal_handler(signal.SIGCHLD, self._sig_chld)
-
-            # Prevent a race condition in case a child terminated
-            # during the switch.
-            self._do_waitpid_all()
-
-    def _sig_chld(self):
-        try:
-            self._do_waitpid_all()
-        except (SystemExit, KeyboardInterrupt):
-            raise
-        except BaseException as exc:
-            # self._loop should always be available here
-            # as '_sig_chld' is added as a signal handler
-            # in 'attach_loop'
-            self._loop.call_exception_handler({
-                'message': 'Unknown exception in SIGCHLD handler',
-                'exception': exc,
-            })
-
-
 class ThreadedChildWatcher(AbstractChildWatcher):
     """Threaded child watcher implementation.
 
@@ -1161,15 +1108,10 @@ class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
 
     def __init__(self):
         super().__init__()
-        self._watcher = None
-
-    def _init_watcher(self):
-        with events._lock:
-            if self._watcher is None:  # pragma: no branch
-                if can_use_pidfd():
-                    self._watcher = PidfdChildWatcher()
-                else:
-                    self._watcher = ThreadedChildWatcher()
+        if can_use_pidfd():
+            self._watcher = PidfdChildWatcher()
+        else:
+            self._watcher = ThreadedChildWatcher()
 
     def set_event_loop(self, loop):
         """Set the event loop.
@@ -1185,33 +1127,6 @@ class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
                 threading.current_thread() is threading.main_thread()):
             self._watcher.attach_loop(loop)
 
-    def get_child_watcher(self):
-        """Get the watcher for child processes.
-
-        If not yet set, a ThreadedChildWatcher object is automatically created.
-        """
-        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):
-        """Set the watcher for child processes."""
-
-        assert watcher is None or isinstance(watcher, AbstractChildWatcher)
-
-        if self._watcher is not None:
-            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
 DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
 EventLoop = SelectorEventLoop
index 06eb4d3841a0d29de653103e8038c08c7dc5b890..5b660de28d6fa0028fde31535a82e20951dbd6a3 100644 (file)
@@ -2212,16 +2212,14 @@ else:
     class UnixEventLoopTestsMixin(EventLoopTestsMixin):
         def setUp(self):
             super().setUp()
-            with warnings.catch_warnings():
-                warnings.simplefilter('ignore', DeprecationWarning)
-                watcher = asyncio.ThreadedChildWatcher()
-                watcher.attach_loop(self.loop)
-                asyncio.set_child_watcher(watcher)
+            watcher = asyncio.ThreadedChildWatcher()
+            watcher.attach_loop(self.loop)
+            policy = asyncio.get_event_loop_policy()
+            policy._watcher = watcher
 
         def tearDown(self):
-            with warnings.catch_warnings():
-                warnings.simplefilter('ignore', DeprecationWarning)
-                asyncio.set_child_watcher(None)
+            policy = asyncio.get_event_loop_policy()
+            policy._watcher = None
             super().tearDown()
 
 
@@ -2716,9 +2714,6 @@ class PolicyTests(unittest.TestCase):
         self.assertRaises(NotImplementedError, policy.get_event_loop)
         self.assertRaises(NotImplementedError, policy.set_event_loop, object())
         self.assertRaises(NotImplementedError, policy.new_event_loop)
-        self.assertRaises(NotImplementedError, policy.get_child_watcher)
-        self.assertRaises(NotImplementedError, policy.set_child_watcher,
-                          object())
 
     def test_get_event_loop(self):
         policy = asyncio.DefaultEventLoopPolicy()
@@ -2836,9 +2831,8 @@ class GetEventLoopTestsMixin:
     def tearDown(self):
         try:
             if sys.platform != 'win32':
-                with warnings.catch_warnings():
-                    warnings.simplefilter('ignore', DeprecationWarning)
-                    asyncio.set_child_watcher(None)
+                policy = asyncio.get_event_loop_policy()
+                policy._watcher = None
 
             super().tearDown()
         finally:
index 27ae766a19413bb4be9728a87eda8c65f0a2fb3d..d7f03e6dd0f4a9c243c7fa5084870442455961e1 100644 (file)
@@ -879,17 +879,13 @@ if sys.platform != 'win32':
 
             watcher = self._get_watcher()
             watcher.attach_loop(self.loop)
-            with warnings.catch_warnings():
-                warnings.simplefilter('ignore', DeprecationWarning)
-                policy.set_child_watcher(watcher)
+            policy._watcher = watcher
 
         def tearDown(self):
             super().tearDown()
             policy = asyncio.get_event_loop_policy()
-            with warnings.catch_warnings():
-                warnings.simplefilter('ignore', DeprecationWarning)
-                watcher = policy.get_child_watcher()
-                policy.set_child_watcher(None)
+            watcher = policy._watcher
+            policy._watcher = None
             watcher.attach_loop(None)
             watcher.close()
 
@@ -910,66 +906,6 @@ if sys.platform != 'win32':
             return unix_events.PidfdChildWatcher()
 
 
-    class GenericWatcherTests(test_utils.TestCase):
-
-        def test_create_subprocess_fails_with_inactive_watcher(self):
-            watcher = mock.create_autospec(asyncio.AbstractChildWatcher)
-            watcher.is_active.return_value = False
-
-            async def execute():
-                asyncio.set_child_watcher(watcher)
-
-                with self.assertRaises(RuntimeError):
-                    await subprocess.create_subprocess_exec(
-                        os_helper.FakePath(sys.executable), '-c', 'pass')
-
-                watcher.add_child_handler.assert_not_called()
-
-            with asyncio.Runner(loop_factory=asyncio.new_event_loop) as runner:
-                with warnings.catch_warnings():
-                    warnings.simplefilter('ignore', DeprecationWarning)
-                    self.assertIsNone(runner.run(execute()))
-            self.assertListEqual(watcher.mock_calls, [
-                mock.call.__enter__(),
-                mock.call.is_active(),
-                mock.call.__exit__(RuntimeError, mock.ANY, mock.ANY),
-            ], watcher.mock_calls)
-
-
-        @unittest.skipUnless(
-            unix_events.can_use_pidfd(),
-            "operating system does not support pidfds",
-        )
-        def test_create_subprocess_with_pidfd(self):
-            async def in_thread():
-                proc = await asyncio.create_subprocess_exec(
-                    *PROGRAM_CAT,
-                    stdin=subprocess.PIPE,
-                    stdout=subprocess.PIPE,
-                )
-                stdout, stderr = await proc.communicate(b"some data")
-                return proc.returncode, stdout
-
-            async def main():
-                # asyncio.Runner did not call asyncio.set_event_loop()
-                with warnings.catch_warnings():
-                    warnings.simplefilter('error', DeprecationWarning)
-                    # get_event_loop() raises DeprecationWarning if
-                    # set_event_loop() was never called and RuntimeError if
-                    # it was called at least once.
-                    with self.assertRaises((RuntimeError, DeprecationWarning)):
-                        asyncio.get_event_loop_policy().get_event_loop()
-                return await asyncio.to_thread(asyncio.run, in_thread())
-            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:
-                with self.assertWarns(DeprecationWarning):
-                    asyncio.set_child_watcher(None)
 else:
     # Windows
     class SubprocessProactorTests(SubprocessMixin, test_utils.TestCase):
index 42fb54a4c3a7a58740a7d6738f4cd8a9fce292fb..2ea698f4d74cf781815f79ad3ba4d9ea060ed898 100644 (file)
@@ -1138,35 +1138,6 @@ class AbstractChildWatcherTests(unittest.TestCase):
             NotImplementedError, watcher.__exit__, f, f, f)
 
 
-class PolicyTests(unittest.TestCase):
-
-    def create_policy(self):
-        return asyncio.DefaultEventLoopPolicy()
-
-    @mock.patch('asyncio.unix_events.can_use_pidfd')
-    def test_get_default_child_watcher(self, m_can_use_pidfd):
-        m_can_use_pidfd.return_value = False
-        policy = self.create_policy()
-        self.assertIsNone(policy._watcher)
-        with self.assertWarns(DeprecationWarning):
-            watcher = policy.get_child_watcher()
-        self.assertIsInstance(watcher, asyncio.ThreadedChildWatcher)
-
-        self.assertIs(policy._watcher, watcher)
-        with self.assertWarns(DeprecationWarning):
-            self.assertIs(watcher, policy.get_child_watcher())
-
-        m_can_use_pidfd.return_value = True
-        policy = self.create_policy()
-        self.assertIsNone(policy._watcher)
-        with self.assertWarns(DeprecationWarning):
-            watcher = policy.get_child_watcher()
-        self.assertIsInstance(watcher, asyncio.PidfdChildWatcher)
-
-        self.assertIs(policy._watcher, watcher)
-        with self.assertWarns(DeprecationWarning):
-            self.assertIs(watcher, policy.get_child_watcher())
-
 class TestFunctional(unittest.TestCase):
 
     def setUp(self):
index 44943e1fa7bc4e0a2a9961360ffce48a31383fa2..3fe2ecd2be6d0c1d2922eb3c5852f412affa236c 100644 (file)
@@ -550,10 +550,8 @@ class TestCase(unittest.TestCase):
         policy = support.maybe_get_event_loop_policy()
         if policy is not None:
             try:
-                with warnings.catch_warnings():
-                    warnings.simplefilter('ignore', DeprecationWarning)
-                    watcher = policy.get_child_watcher()
-            except NotImplementedError:
+                watcher = policy._watcher
+            except AttributeError:
                 # watcher is not implemented by EventLoopPolicy, e.g. Windows
                 pass
             else: