mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +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 | ||||
| ================ | ||||
| 
 | ||||
| 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 | ||||
| ------- | ||||
| 
 | ||||
|  |  | |||
|  | @ -1022,6 +1022,13 @@ class SafeChildWatcher(BaseChildWatcher): | |||
|     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): | ||||
|         self._callbacks.clear() | ||||
|         super().close() | ||||
|  | @ -1100,6 +1107,10 @@ def __init__(self): | |||
|         self._lock = threading.Lock() | ||||
|         self._zombies = {} | ||||
|         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): | ||||
|         self._callbacks.clear() | ||||
|  | @ -1212,6 +1223,10 @@ class MultiLoopChildWatcher(AbstractChildWatcher): | |||
|     def __init__(self): | ||||
|         self._callbacks = {} | ||||
|         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): | ||||
|         return self._saved_sighandler is not None | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ | |||
| import unittest | ||||
| from unittest import mock | ||||
| import weakref | ||||
| 
 | ||||
| import warnings | ||||
| if sys.platform not in ('win32', 'vxworks'): | ||||
|     import tty | ||||
| 
 | ||||
|  | @ -2055,6 +2055,8 @@ def test_remove_fds_after_closing(self): | |||
|     class UnixEventLoopTestsMixin(EventLoopTestsMixin): | ||||
|         def setUp(self): | ||||
|             super().setUp() | ||||
|             with warnings.catch_warnings(): | ||||
|                 warnings.simplefilter('ignore', DeprecationWarning) | ||||
|                 watcher = asyncio.SafeChildWatcher() | ||||
|             watcher.attach_loop(self.loop) | ||||
|             asyncio.set_child_watcher(watcher) | ||||
|  | @ -2652,6 +2654,8 @@ def setUp(self): | |||
|         asyncio.set_event_loop(self.loop) | ||||
| 
 | ||||
|         if sys.platform != 'win32': | ||||
|             with warnings.catch_warnings(): | ||||
|                 warnings.simplefilter('ignore', DeprecationWarning) | ||||
|                 watcher = asyncio.SafeChildWatcher() | ||||
|             watcher.attach_loop(self.loop) | ||||
|             asyncio.set_child_watcher(watcher) | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| import threading | ||||
| import unittest | ||||
| from unittest import mock | ||||
| import warnings | ||||
| from test.support import socket_helper | ||||
| try: | ||||
|     import ssl | ||||
|  | @ -791,7 +792,8 @@ def test_read_all_from_pipe_reader(self): | |||
|         protocol = asyncio.StreamReaderProtocol(reader, loop=self.loop) | ||||
|         transport, _ = self.loop.run_until_complete( | ||||
|             self.loop.connect_read_pipe(lambda: protocol, pipe)) | ||||
| 
 | ||||
|         with warnings.catch_warnings(): | ||||
|             warnings.simplefilter('ignore', DeprecationWarning) | ||||
|             watcher = asyncio.SafeChildWatcher() | ||||
|         watcher.attach_loop(self.loop) | ||||
|         try: | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ | |||
| import sys | ||||
| import unittest | ||||
| import warnings | ||||
| import functools | ||||
| from unittest import mock | ||||
| 
 | ||||
| import asyncio | ||||
|  | @ -31,19 +30,6 @@ | |||
|               '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(): | ||||
|     asyncio.set_event_loop_policy(None) | ||||
| 
 | ||||
|  | @ -688,7 +674,7 @@ def setUp(self): | |||
|             self.loop = policy.new_event_loop() | ||||
|             self.set_event_loop(self.loop) | ||||
| 
 | ||||
|             watcher = self.Watcher() | ||||
|             watcher = self._get_watcher() | ||||
|             watcher.attach_loop(self.loop) | ||||
|             policy.set_child_watcher(watcher) | ||||
| 
 | ||||
|  | @ -703,32 +689,38 @@ def tearDown(self): | |||
|     class SubprocessThreadedWatcherTests(SubprocessWatcherMixin, | ||||
|                                          test_utils.TestCase): | ||||
| 
 | ||||
|         Watcher = 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 | ||||
|         def _get_watcher(self): | ||||
|             return unix_events.ThreadedChildWatcher() | ||||
| 
 | ||||
|     class SubprocessSafeWatcherTests(SubprocessWatcherMixin, | ||||
|                                      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, | ||||
|                                      test_utils.TestCase): | ||||
| 
 | ||||
|         Watcher = unix_events.FastChildWatcher | ||||
|         def _get_watcher(self): | ||||
|             with self.assertWarns(DeprecationWarning): | ||||
|                 return unix_events.FastChildWatcher() | ||||
| 
 | ||||
|     @unittest.skipUnless( | ||||
|         _has_pidfd_support(), | ||||
|         unix_events.can_use_pidfd(), | ||||
|         "operating system does not support pidfds", | ||||
|     ) | ||||
|     class SubprocessPidfdWatcherTests(SubprocessWatcherMixin, | ||||
|                                       test_utils.TestCase): | ||||
|         Watcher = unix_events.PidfdChildWatcher | ||||
| 
 | ||||
|         def _get_watcher(self): | ||||
|             return unix_events.PidfdChildWatcher() | ||||
| 
 | ||||
| 
 | ||||
|     class GenericWatcherTests(test_utils.TestCase): | ||||
|  | @ -758,7 +750,7 @@ async def execute(): | |||
| 
 | ||||
| 
 | ||||
|         @unittest.skipUnless( | ||||
|             _has_pidfd_support(), | ||||
|             unix_events.can_use_pidfd(), | ||||
|             "operating system does not support pidfds", | ||||
|         ) | ||||
|         def test_create_subprocess_with_pidfd(self): | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ | |||
| import threading | ||||
| import unittest | ||||
| from unittest import mock | ||||
| import warnings | ||||
| from test.support import os_helper | ||||
| from test.support import socket_helper | ||||
| 
 | ||||
|  | @ -1686,11 +1687,15 @@ def test_close(self, m_waitpid): | |||
| 
 | ||||
| class SafeChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): | ||||
|     def create_watcher(self): | ||||
|         with warnings.catch_warnings(): | ||||
|             warnings.simplefilter("ignore", DeprecationWarning) | ||||
|             return asyncio.SafeChildWatcher() | ||||
| 
 | ||||
| 
 | ||||
| class FastChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): | ||||
|     def create_watcher(self): | ||||
|         with warnings.catch_warnings(): | ||||
|             warnings.simplefilter("ignore", DeprecationWarning) | ||||
|             return asyncio.FastChildWatcher() | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1724,6 +1729,8 @@ def test_get_default_child_watcher(self): | |||
| 
 | ||||
|     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) | ||||
|  | @ -1745,6 +1752,8 @@ def f(): | |||
|             policy.get_event_loop().close() | ||||
| 
 | ||||
|         policy = self.create_policy() | ||||
|         with warnings.catch_warnings(): | ||||
|             warnings.simplefilter("ignore", DeprecationWarning) | ||||
|             policy.set_child_watcher(asyncio.SafeChildWatcher()) | ||||
| 
 | ||||
|         th = threading.Thread(target=f) | ||||
|  | @ -1757,6 +1766,8 @@ def test_child_watcher_replace_mainloop_existing(self): | |||
| 
 | ||||
|         # Explicitly setup SafeChildWatcher, | ||||
|         # default ThreadedChildWatcher has no _loop property | ||||
|         with warnings.catch_warnings(): | ||||
|             warnings.simplefilter("ignore", DeprecationWarning) | ||||
|             watcher = asyncio.SafeChildWatcher() | ||||
|         policy.set_child_watcher(watcher) | ||||
|         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