mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	GH-94597: deprecate SafeChildWatcher, FastChildWatcher and MultiLoopChildWatcher child watchers  (#98089)
				
					
				
			This commit is contained in:
		
							parent
							
								
									75751f4aa5
								
							
						
					
					
						commit
						d8765284f3
					
				
					 7 changed files with 81 additions and 38 deletions
				
			
		|  | @ -109,6 +109,24 @@ New Modules | ||||||
| Improved Modules | Improved Modules | ||||||
| ================ | ================ | ||||||
| 
 | 
 | ||||||
|  | asyncio | ||||||
|  | ------- | ||||||
|  | 
 | ||||||
|  | * On Linux, :mod:`asyncio` uses :class:`~asyncio.PidfdChildWatcher` by default | ||||||
|  |   if :func:`os.pidfd_open` is available and functional instead of | ||||||
|  |   :class:`~asyncio.ThreadedChildWatcher`. | ||||||
|  |   (Contributed by Kumar Aditya in :gh:`98024`.) | ||||||
|  | 
 | ||||||
|  | * The child watcher classes :class:`~asyncio.MultiLoopChildWatcher`, | ||||||
|  |   :class:`~asyncio.FastChildWatcher` and | ||||||
|  |   :class:`~asyncio.SafeChildWatcher` are deprecated and | ||||||
|  |   will be removed in Python 3.14. It is recommended to not manually | ||||||
|  |   configure a child watcher as the event loop now uses the best available | ||||||
|  |   child watcher for each platform (:class:`~asyncio.PidfdChildWatcher` | ||||||
|  |   if supported and :class:`~asyncio.ThreadedChildWatcher` otherwise). | ||||||
|  |   (Contributed by Kumar Aditya in :gh:`94597`.) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| pathlib | pathlib | ||||||
| ------- | ------- | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1022,6 +1022,13 @@ class SafeChildWatcher(BaseChildWatcher): | ||||||
|     big number of children (O(n) each time SIGCHLD is raised) |     big number of children (O(n) each time SIGCHLD is raised) | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         super().__init__() | ||||||
|  |         warnings._deprecated("SafeChildWatcher", | ||||||
|  |                              "{name!r} is deprecated as of Python 3.12 and will be " | ||||||
|  |                              "removed in Python {remove}.", | ||||||
|  |                               remove=(3, 14)) | ||||||
|  | 
 | ||||||
|     def close(self): |     def close(self): | ||||||
|         self._callbacks.clear() |         self._callbacks.clear() | ||||||
|         super().close() |         super().close() | ||||||
|  | @ -1100,6 +1107,10 @@ def __init__(self): | ||||||
|         self._lock = threading.Lock() |         self._lock = threading.Lock() | ||||||
|         self._zombies = {} |         self._zombies = {} | ||||||
|         self._forks = 0 |         self._forks = 0 | ||||||
|  |         warnings._deprecated("FastChildWatcher", | ||||||
|  |                              "{name!r} is deprecated as of Python 3.12 and will be " | ||||||
|  |                              "removed in Python {remove}.", | ||||||
|  |                               remove=(3, 14)) | ||||||
| 
 | 
 | ||||||
|     def close(self): |     def close(self): | ||||||
|         self._callbacks.clear() |         self._callbacks.clear() | ||||||
|  | @ -1212,6 +1223,10 @@ class MultiLoopChildWatcher(AbstractChildWatcher): | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self._callbacks = {} |         self._callbacks = {} | ||||||
|         self._saved_sighandler = None |         self._saved_sighandler = None | ||||||
|  |         warnings._deprecated("MultiLoopChildWatcher", | ||||||
|  |                              "{name!r} is deprecated as of Python 3.12 and will be " | ||||||
|  |                              "removed in Python {remove}.", | ||||||
|  |                               remove=(3, 14)) | ||||||
| 
 | 
 | ||||||
|     def is_active(self): |     def is_active(self): | ||||||
|         return self._saved_sighandler is not None |         return self._saved_sighandler is not None | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ | ||||||
| import unittest | import unittest | ||||||
| from unittest import mock | from unittest import mock | ||||||
| import weakref | import weakref | ||||||
| 
 | import warnings | ||||||
| if sys.platform not in ('win32', 'vxworks'): | if sys.platform not in ('win32', 'vxworks'): | ||||||
|     import tty |     import tty | ||||||
| 
 | 
 | ||||||
|  | @ -2055,7 +2055,9 @@ def test_remove_fds_after_closing(self): | ||||||
|     class UnixEventLoopTestsMixin(EventLoopTestsMixin): |     class UnixEventLoopTestsMixin(EventLoopTestsMixin): | ||||||
|         def setUp(self): |         def setUp(self): | ||||||
|             super().setUp() |             super().setUp() | ||||||
|             watcher = asyncio.SafeChildWatcher() |             with warnings.catch_warnings(): | ||||||
|  |                 warnings.simplefilter('ignore', DeprecationWarning) | ||||||
|  |                 watcher = asyncio.SafeChildWatcher() | ||||||
|             watcher.attach_loop(self.loop) |             watcher.attach_loop(self.loop) | ||||||
|             asyncio.set_child_watcher(watcher) |             asyncio.set_child_watcher(watcher) | ||||||
| 
 | 
 | ||||||
|  | @ -2652,7 +2654,9 @@ def setUp(self): | ||||||
|         asyncio.set_event_loop(self.loop) |         asyncio.set_event_loop(self.loop) | ||||||
| 
 | 
 | ||||||
|         if sys.platform != 'win32': |         if sys.platform != 'win32': | ||||||
|             watcher = asyncio.SafeChildWatcher() |             with warnings.catch_warnings(): | ||||||
|  |                 warnings.simplefilter('ignore', DeprecationWarning) | ||||||
|  |                 watcher = asyncio.SafeChildWatcher() | ||||||
|             watcher.attach_loop(self.loop) |             watcher.attach_loop(self.loop) | ||||||
|             asyncio.set_child_watcher(watcher) |             asyncio.set_child_watcher(watcher) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| import threading | import threading | ||||||
| import unittest | import unittest | ||||||
| from unittest import mock | from unittest import mock | ||||||
|  | import warnings | ||||||
| from test.support import socket_helper | from test.support import socket_helper | ||||||
| try: | try: | ||||||
|     import ssl |     import ssl | ||||||
|  | @ -791,8 +792,9 @@ def test_read_all_from_pipe_reader(self): | ||||||
|         protocol = asyncio.StreamReaderProtocol(reader, loop=self.loop) |         protocol = asyncio.StreamReaderProtocol(reader, loop=self.loop) | ||||||
|         transport, _ = self.loop.run_until_complete( |         transport, _ = self.loop.run_until_complete( | ||||||
|             self.loop.connect_read_pipe(lambda: protocol, pipe)) |             self.loop.connect_read_pipe(lambda: protocol, pipe)) | ||||||
| 
 |         with warnings.catch_warnings(): | ||||||
|         watcher = asyncio.SafeChildWatcher() |             warnings.simplefilter('ignore', DeprecationWarning) | ||||||
|  |             watcher = asyncio.SafeChildWatcher() | ||||||
|         watcher.attach_loop(self.loop) |         watcher.attach_loop(self.loop) | ||||||
|         try: |         try: | ||||||
|             asyncio.set_child_watcher(watcher) |             asyncio.set_child_watcher(watcher) | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ | ||||||
| import sys | import sys | ||||||
| import unittest | import unittest | ||||||
| import warnings | import warnings | ||||||
| import functools |  | ||||||
| from unittest import mock | from unittest import mock | ||||||
| 
 | 
 | ||||||
| import asyncio | import asyncio | ||||||
|  | @ -31,19 +30,6 @@ | ||||||
|               'sys.stdout.buffer.write(data)'))] |               'sys.stdout.buffer.write(data)'))] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @functools.cache |  | ||||||
| def _has_pidfd_support(): |  | ||||||
|     if not hasattr(os, 'pidfd_open'): |  | ||||||
|         return False |  | ||||||
| 
 |  | ||||||
|     try: |  | ||||||
|         os.close(os.pidfd_open(os.getpid())) |  | ||||||
|     except OSError: |  | ||||||
|         return False |  | ||||||
| 
 |  | ||||||
|     return True |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def tearDownModule(): | def tearDownModule(): | ||||||
|     asyncio.set_event_loop_policy(None) |     asyncio.set_event_loop_policy(None) | ||||||
| 
 | 
 | ||||||
|  | @ -688,7 +674,7 @@ def setUp(self): | ||||||
|             self.loop = policy.new_event_loop() |             self.loop = policy.new_event_loop() | ||||||
|             self.set_event_loop(self.loop) |             self.set_event_loop(self.loop) | ||||||
| 
 | 
 | ||||||
|             watcher = self.Watcher() |             watcher = self._get_watcher() | ||||||
|             watcher.attach_loop(self.loop) |             watcher.attach_loop(self.loop) | ||||||
|             policy.set_child_watcher(watcher) |             policy.set_child_watcher(watcher) | ||||||
| 
 | 
 | ||||||
|  | @ -703,32 +689,38 @@ def tearDown(self): | ||||||
|     class SubprocessThreadedWatcherTests(SubprocessWatcherMixin, |     class SubprocessThreadedWatcherTests(SubprocessWatcherMixin, | ||||||
|                                          test_utils.TestCase): |                                          test_utils.TestCase): | ||||||
| 
 | 
 | ||||||
|         Watcher = unix_events.ThreadedChildWatcher |         def _get_watcher(self): | ||||||
| 
 |             return unix_events.ThreadedChildWatcher() | ||||||
|     @unittest.skip("bpo-38323: MultiLoopChildWatcher has a race condition \ |  | ||||||
|                     and these tests can hang the test suite") |  | ||||||
|     class SubprocessMultiLoopWatcherTests(SubprocessWatcherMixin, |  | ||||||
|                                           test_utils.TestCase): |  | ||||||
| 
 |  | ||||||
|         Watcher = unix_events.MultiLoopChildWatcher |  | ||||||
| 
 | 
 | ||||||
|     class SubprocessSafeWatcherTests(SubprocessWatcherMixin, |     class SubprocessSafeWatcherTests(SubprocessWatcherMixin, | ||||||
|                                      test_utils.TestCase): |                                      test_utils.TestCase): | ||||||
| 
 | 
 | ||||||
|         Watcher = unix_events.SafeChildWatcher |         def _get_watcher(self): | ||||||
|  |             with self.assertWarns(DeprecationWarning): | ||||||
|  |                 return unix_events.SafeChildWatcher() | ||||||
|  | 
 | ||||||
|  |     class MultiLoopChildWatcherTests(test_utils.TestCase): | ||||||
|  | 
 | ||||||
|  |         def test_warns(self): | ||||||
|  |             with self.assertWarns(DeprecationWarning): | ||||||
|  |                 unix_events.MultiLoopChildWatcher() | ||||||
| 
 | 
 | ||||||
|     class SubprocessFastWatcherTests(SubprocessWatcherMixin, |     class SubprocessFastWatcherTests(SubprocessWatcherMixin, | ||||||
|                                      test_utils.TestCase): |                                      test_utils.TestCase): | ||||||
| 
 | 
 | ||||||
|         Watcher = unix_events.FastChildWatcher |         def _get_watcher(self): | ||||||
|  |             with self.assertWarns(DeprecationWarning): | ||||||
|  |                 return unix_events.FastChildWatcher() | ||||||
| 
 | 
 | ||||||
|     @unittest.skipUnless( |     @unittest.skipUnless( | ||||||
|         _has_pidfd_support(), |         unix_events.can_use_pidfd(), | ||||||
|         "operating system does not support pidfds", |         "operating system does not support pidfds", | ||||||
|     ) |     ) | ||||||
|     class SubprocessPidfdWatcherTests(SubprocessWatcherMixin, |     class SubprocessPidfdWatcherTests(SubprocessWatcherMixin, | ||||||
|                                       test_utils.TestCase): |                                       test_utils.TestCase): | ||||||
|         Watcher = unix_events.PidfdChildWatcher | 
 | ||||||
|  |         def _get_watcher(self): | ||||||
|  |             return unix_events.PidfdChildWatcher() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     class GenericWatcherTests(test_utils.TestCase): |     class GenericWatcherTests(test_utils.TestCase): | ||||||
|  | @ -758,7 +750,7 @@ async def execute(): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         @unittest.skipUnless( |         @unittest.skipUnless( | ||||||
|             _has_pidfd_support(), |             unix_events.can_use_pidfd(), | ||||||
|             "operating system does not support pidfds", |             "operating system does not support pidfds", | ||||||
|         ) |         ) | ||||||
|         def test_create_subprocess_with_pidfd(self): |         def test_create_subprocess_with_pidfd(self): | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| import threading | import threading | ||||||
| import unittest | import unittest | ||||||
| from unittest import mock | from unittest import mock | ||||||
|  | import warnings | ||||||
| from test.support import os_helper | from test.support import os_helper | ||||||
| from test.support import socket_helper | from test.support import socket_helper | ||||||
| 
 | 
 | ||||||
|  | @ -1686,12 +1687,16 @@ def test_close(self, m_waitpid): | ||||||
| 
 | 
 | ||||||
| class SafeChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): | class SafeChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): | ||||||
|     def create_watcher(self): |     def create_watcher(self): | ||||||
|         return asyncio.SafeChildWatcher() |         with warnings.catch_warnings(): | ||||||
|  |             warnings.simplefilter("ignore", DeprecationWarning) | ||||||
|  |             return asyncio.SafeChildWatcher() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class FastChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): | class FastChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): | ||||||
|     def create_watcher(self): |     def create_watcher(self): | ||||||
|         return asyncio.FastChildWatcher() |         with warnings.catch_warnings(): | ||||||
|  |             warnings.simplefilter("ignore", DeprecationWarning) | ||||||
|  |             return asyncio.FastChildWatcher() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class PolicyTests(unittest.TestCase): | class PolicyTests(unittest.TestCase): | ||||||
|  | @ -1724,7 +1729,9 @@ def test_get_default_child_watcher(self): | ||||||
| 
 | 
 | ||||||
|     def test_get_child_watcher_after_set(self): |     def test_get_child_watcher_after_set(self): | ||||||
|         policy = self.create_policy() |         policy = self.create_policy() | ||||||
|         watcher = asyncio.FastChildWatcher() |         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(policy._watcher, watcher) | ||||||
|  | @ -1745,7 +1752,9 @@ def f(): | ||||||
|             policy.get_event_loop().close() |             policy.get_event_loop().close() | ||||||
| 
 | 
 | ||||||
|         policy = self.create_policy() |         policy = self.create_policy() | ||||||
|         policy.set_child_watcher(asyncio.SafeChildWatcher()) |         with warnings.catch_warnings(): | ||||||
|  |             warnings.simplefilter("ignore", DeprecationWarning) | ||||||
|  |             policy.set_child_watcher(asyncio.SafeChildWatcher()) | ||||||
| 
 | 
 | ||||||
|         th = threading.Thread(target=f) |         th = threading.Thread(target=f) | ||||||
|         th.start() |         th.start() | ||||||
|  | @ -1757,7 +1766,9 @@ def test_child_watcher_replace_mainloop_existing(self): | ||||||
| 
 | 
 | ||||||
|         # Explicitly setup SafeChildWatcher, |         # Explicitly setup SafeChildWatcher, | ||||||
|         # default ThreadedChildWatcher has no _loop property |         # default ThreadedChildWatcher has no _loop property | ||||||
|         watcher = asyncio.SafeChildWatcher() |         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) |         watcher.attach_loop(loop) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | The child watcher classes :class:`~asyncio.MultiLoopChildWatcher`, :class:`~asyncio.FastChildWatcher` and :class:`~asyncio.SafeChildWatcher` are deprecated and will be removed in Python 3.14. Patch by Kumar Aditya. | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Kumar Aditya
						Kumar Aditya