mirror of
https://github.com/python/cpython.git
synced 2025-09-20 07:31:10 +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 @@ class FastChildWatcher(BaseChildWatcher):
|
||||||
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 errno
|
||||||
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,6 +2055,8 @@ else:
|
||||||
class UnixEventLoopTestsMixin(EventLoopTestsMixin):
|
class UnixEventLoopTestsMixin(EventLoopTestsMixin):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter('ignore', DeprecationWarning)
|
||||||
watcher = asyncio.SafeChildWatcher()
|
watcher = asyncio.SafeChildWatcher()
|
||||||
watcher.attach_loop(self.loop)
|
watcher.attach_loop(self.loop)
|
||||||
asyncio.set_child_watcher(watcher)
|
asyncio.set_child_watcher(watcher)
|
||||||
|
@ -2652,6 +2654,8 @@ class GetEventLoopTestsMixin:
|
||||||
asyncio.set_event_loop(self.loop)
|
asyncio.set_event_loop(self.loop)
|
||||||
|
|
||||||
if sys.platform != 'win32':
|
if sys.platform != 'win32':
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter('ignore', DeprecationWarning)
|
||||||
watcher = asyncio.SafeChildWatcher()
|
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 sys
|
||||||
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,7 +792,8 @@ os.close(fd)
|
||||||
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():
|
||||||
|
warnings.simplefilter('ignore', DeprecationWarning)
|
||||||
watcher = asyncio.SafeChildWatcher()
|
watcher = asyncio.SafeChildWatcher()
|
||||||
watcher.attach_loop(self.loop)
|
watcher.attach_loop(self.loop)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -4,7 +4,6 @@ import signal
|
||||||
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 @@ PROGRAM_CAT = [
|
||||||
'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 @@ if sys.platform != 'win32':
|
||||||
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 @@ if sys.platform != 'win32':
|
||||||
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 @@ if sys.platform != 'win32':
|
||||||
|
|
||||||
|
|
||||||
@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 sys
|
||||||
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,11 +1687,15 @@ class ChildWatcherTestsMixin:
|
||||||
|
|
||||||
class SafeChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase):
|
class SafeChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase):
|
||||||
def create_watcher(self):
|
def create_watcher(self):
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", DeprecationWarning)
|
||||||
return asyncio.SafeChildWatcher()
|
return asyncio.SafeChildWatcher()
|
||||||
|
|
||||||
|
|
||||||
class FastChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase):
|
class FastChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase):
|
||||||
def create_watcher(self):
|
def create_watcher(self):
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", DeprecationWarning)
|
||||||
return asyncio.FastChildWatcher()
|
return asyncio.FastChildWatcher()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1724,6 +1729,8 @@ class PolicyTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_get_child_watcher_after_set(self):
|
def test_get_child_watcher_after_set(self):
|
||||||
policy = self.create_policy()
|
policy = self.create_policy()
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", DeprecationWarning)
|
||||||
watcher = asyncio.FastChildWatcher()
|
watcher = asyncio.FastChildWatcher()
|
||||||
|
|
||||||
policy.set_child_watcher(watcher)
|
policy.set_child_watcher(watcher)
|
||||||
|
@ -1745,6 +1752,8 @@ class PolicyTests(unittest.TestCase):
|
||||||
policy.get_event_loop().close()
|
policy.get_event_loop().close()
|
||||||
|
|
||||||
policy = self.create_policy()
|
policy = self.create_policy()
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", DeprecationWarning)
|
||||||
policy.set_child_watcher(asyncio.SafeChildWatcher())
|
policy.set_child_watcher(asyncio.SafeChildWatcher())
|
||||||
|
|
||||||
th = threading.Thread(target=f)
|
th = threading.Thread(target=f)
|
||||||
|
@ -1757,6 +1766,8 @@ class PolicyTests(unittest.TestCase):
|
||||||
|
|
||||||
# Explicitly setup SafeChildWatcher,
|
# Explicitly setup SafeChildWatcher,
|
||||||
# default ThreadedChildWatcher has no _loop property
|
# default ThreadedChildWatcher has no _loop property
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore", DeprecationWarning)
|
||||||
watcher = asyncio.SafeChildWatcher()
|
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