Skip to content

Commit d876528

Browse files
authoredOct 8, 2022
GH-94597: deprecate SafeChildWatcher, FastChildWatcher and MultiLoopChildWatcher child watchers (#98089)
1 parent 75751f4 commit d876528

File tree

7 files changed

+81
-38
lines changed

7 files changed

+81
-38
lines changed
 

‎Doc/whatsnew/3.12.rst

+18
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,24 @@ New Modules
109109
Improved Modules
110110
================
111111

112+
asyncio
113+
-------
114+
115+
* On Linux, :mod:`asyncio` uses :class:`~asyncio.PidfdChildWatcher` by default
116+
if :func:`os.pidfd_open` is available and functional instead of
117+
:class:`~asyncio.ThreadedChildWatcher`.
118+
(Contributed by Kumar Aditya in :gh:`98024`.)
119+
120+
* The child watcher classes :class:`~asyncio.MultiLoopChildWatcher`,
121+
:class:`~asyncio.FastChildWatcher` and
122+
:class:`~asyncio.SafeChildWatcher` are deprecated and
123+
will be removed in Python 3.14. It is recommended to not manually
124+
configure a child watcher as the event loop now uses the best available
125+
child watcher for each platform (:class:`~asyncio.PidfdChildWatcher`
126+
if supported and :class:`~asyncio.ThreadedChildWatcher` otherwise).
127+
(Contributed by Kumar Aditya in :gh:`94597`.)
128+
129+
112130
pathlib
113131
-------
114132

‎Lib/asyncio/unix_events.py

+15
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,13 @@ class SafeChildWatcher(BaseChildWatcher):
10221022
big number of children (O(n) each time SIGCHLD is raised)
10231023
"""
10241024

1025+
def __init__(self):
1026+
super().__init__()
1027+
warnings._deprecated("SafeChildWatcher",
1028+
"{name!r} is deprecated as of Python 3.12 and will be "
1029+
"removed in Python {remove}.",
1030+
remove=(3, 14))
1031+
10251032
def close(self):
10261033
self._callbacks.clear()
10271034
super().close()
@@ -1100,6 +1107,10 @@ def __init__(self):
11001107
self._lock = threading.Lock()
11011108
self._zombies = {}
11021109
self._forks = 0
1110+
warnings._deprecated("FastChildWatcher",
1111+
"{name!r} is deprecated as of Python 3.12 and will be "
1112+
"removed in Python {remove}.",
1113+
remove=(3, 14))
11031114

11041115
def close(self):
11051116
self._callbacks.clear()
@@ -1212,6 +1223,10 @@ class MultiLoopChildWatcher(AbstractChildWatcher):
12121223
def __init__(self):
12131224
self._callbacks = {}
12141225
self._saved_sighandler = None
1226+
warnings._deprecated("MultiLoopChildWatcher",
1227+
"{name!r} is deprecated as of Python 3.12 and will be "
1228+
"removed in Python {remove}.",
1229+
remove=(3, 14))
12151230

12161231
def is_active(self):
12171232
return self._saved_sighandler is not None

‎Lib/test/test_asyncio/test_events.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import unittest
2323
from unittest import mock
2424
import weakref
25-
25+
import warnings
2626
if sys.platform not in ('win32', 'vxworks'):
2727
import tty
2828

@@ -2055,7 +2055,9 @@ def test_remove_fds_after_closing(self):
20552055
class UnixEventLoopTestsMixin(EventLoopTestsMixin):
20562056
def setUp(self):
20572057
super().setUp()
2058-
watcher = asyncio.SafeChildWatcher()
2058+
with warnings.catch_warnings():
2059+
warnings.simplefilter('ignore', DeprecationWarning)
2060+
watcher = asyncio.SafeChildWatcher()
20592061
watcher.attach_loop(self.loop)
20602062
asyncio.set_child_watcher(watcher)
20612063

@@ -2652,7 +2654,9 @@ def setUp(self):
26522654
asyncio.set_event_loop(self.loop)
26532655

26542656
if sys.platform != 'win32':
2655-
watcher = asyncio.SafeChildWatcher()
2657+
with warnings.catch_warnings():
2658+
warnings.simplefilter('ignore', DeprecationWarning)
2659+
watcher = asyncio.SafeChildWatcher()
26562660
watcher.attach_loop(self.loop)
26572661
asyncio.set_child_watcher(watcher)
26582662

‎Lib/test/test_asyncio/test_streams.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import threading
1010
import unittest
1111
from unittest import mock
12+
import warnings
1213
from test.support import socket_helper
1314
try:
1415
import ssl
@@ -791,8 +792,9 @@ def test_read_all_from_pipe_reader(self):
791792
protocol = asyncio.StreamReaderProtocol(reader, loop=self.loop)
792793
transport, _ = self.loop.run_until_complete(
793794
self.loop.connect_read_pipe(lambda: protocol, pipe))
794-
795-
watcher = asyncio.SafeChildWatcher()
795+
with warnings.catch_warnings():
796+
warnings.simplefilter('ignore', DeprecationWarning)
797+
watcher = asyncio.SafeChildWatcher()
796798
watcher.attach_loop(self.loop)
797799
try:
798800
asyncio.set_child_watcher(watcher)

‎Lib/test/test_asyncio/test_subprocess.py

+20-28
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import sys
55
import unittest
66
import warnings
7-
import functools
87
from unittest import mock
98

109
import asyncio
@@ -31,19 +30,6 @@
3130
'sys.stdout.buffer.write(data)'))]
3231

3332

34-
@functools.cache
35-
def _has_pidfd_support():
36-
if not hasattr(os, 'pidfd_open'):
37-
return False
38-
39-
try:
40-
os.close(os.pidfd_open(os.getpid()))
41-
except OSError:
42-
return False
43-
44-
return True
45-
46-
4733
def tearDownModule():
4834
asyncio.set_event_loop_policy(None)
4935

@@ -688,7 +674,7 @@ def setUp(self):
688674
self.loop = policy.new_event_loop()
689675
self.set_event_loop(self.loop)
690676

691-
watcher = self.Watcher()
677+
watcher = self._get_watcher()
692678
watcher.attach_loop(self.loop)
693679
policy.set_child_watcher(watcher)
694680

@@ -703,32 +689,38 @@ def tearDown(self):
703689
class SubprocessThreadedWatcherTests(SubprocessWatcherMixin,
704690
test_utils.TestCase):
705691

706-
Watcher = unix_events.ThreadedChildWatcher
707-
708-
@unittest.skip("bpo-38323: MultiLoopChildWatcher has a race condition \
709-
and these tests can hang the test suite")
710-
class SubprocessMultiLoopWatcherTests(SubprocessWatcherMixin,
711-
test_utils.TestCase):
712-
713-
Watcher = unix_events.MultiLoopChildWatcher
692+
def _get_watcher(self):
693+
return unix_events.ThreadedChildWatcher()
714694

715695
class SubprocessSafeWatcherTests(SubprocessWatcherMixin,
716696
test_utils.TestCase):
717697

718-
Watcher = unix_events.SafeChildWatcher
698+
def _get_watcher(self):
699+
with self.assertWarns(DeprecationWarning):
700+
return unix_events.SafeChildWatcher()
701+
702+
class MultiLoopChildWatcherTests(test_utils.TestCase):
703+
704+
def test_warns(self):
705+
with self.assertWarns(DeprecationWarning):
706+
unix_events.MultiLoopChildWatcher()
719707

720708
class SubprocessFastWatcherTests(SubprocessWatcherMixin,
721709
test_utils.TestCase):
722710

723-
Watcher = unix_events.FastChildWatcher
711+
def _get_watcher(self):
712+
with self.assertWarns(DeprecationWarning):
713+
return unix_events.FastChildWatcher()
724714

725715
@unittest.skipUnless(
726-
_has_pidfd_support(),
716+
unix_events.can_use_pidfd(),
727717
"operating system does not support pidfds",
728718
)
729719
class SubprocessPidfdWatcherTests(SubprocessWatcherMixin,
730720
test_utils.TestCase):
731-
Watcher = unix_events.PidfdChildWatcher
721+
722+
def _get_watcher(self):
723+
return unix_events.PidfdChildWatcher()
732724

733725

734726
class GenericWatcherTests(test_utils.TestCase):
@@ -758,7 +750,7 @@ async def execute():
758750

759751

760752
@unittest.skipUnless(
761-
_has_pidfd_support(),
753+
unix_events.can_use_pidfd(),
762754
"operating system does not support pidfds",
763755
)
764756
def test_create_subprocess_with_pidfd(self):

‎Lib/test/test_asyncio/test_unix_events.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import threading
1313
import unittest
1414
from unittest import mock
15+
import warnings
1516
from test.support import os_helper
1617
from test.support import socket_helper
1718

@@ -1686,12 +1687,16 @@ def test_close(self, m_waitpid):
16861687

16871688
class SafeChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase):
16881689
def create_watcher(self):
1689-
return asyncio.SafeChildWatcher()
1690+
with warnings.catch_warnings():
1691+
warnings.simplefilter("ignore", DeprecationWarning)
1692+
return asyncio.SafeChildWatcher()
16901693

16911694

16921695
class FastChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase):
16931696
def create_watcher(self):
1694-
return asyncio.FastChildWatcher()
1697+
with warnings.catch_warnings():
1698+
warnings.simplefilter("ignore", DeprecationWarning)
1699+
return asyncio.FastChildWatcher()
16951700

16961701

16971702
class PolicyTests(unittest.TestCase):
@@ -1724,7 +1729,9 @@ def test_get_default_child_watcher(self):
17241729

17251730
def test_get_child_watcher_after_set(self):
17261731
policy = self.create_policy()
1727-
watcher = asyncio.FastChildWatcher()
1732+
with warnings.catch_warnings():
1733+
warnings.simplefilter("ignore", DeprecationWarning)
1734+
watcher = asyncio.FastChildWatcher()
17281735

17291736
policy.set_child_watcher(watcher)
17301737
self.assertIs(policy._watcher, watcher)
@@ -1745,7 +1752,9 @@ def f():
17451752
policy.get_event_loop().close()
17461753

17471754
policy = self.create_policy()
1748-
policy.set_child_watcher(asyncio.SafeChildWatcher())
1755+
with warnings.catch_warnings():
1756+
warnings.simplefilter("ignore", DeprecationWarning)
1757+
policy.set_child_watcher(asyncio.SafeChildWatcher())
17491758

17501759
th = threading.Thread(target=f)
17511760
th.start()
@@ -1757,7 +1766,9 @@ def test_child_watcher_replace_mainloop_existing(self):
17571766

17581767
# Explicitly setup SafeChildWatcher,
17591768
# default ThreadedChildWatcher has no _loop property
1760-
watcher = asyncio.SafeChildWatcher()
1769+
with warnings.catch_warnings():
1770+
warnings.simplefilter("ignore", DeprecationWarning)
1771+
watcher = asyncio.SafeChildWatcher()
17611772
policy.set_child_watcher(watcher)
17621773
watcher.attach_loop(loop)
17631774

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

0 commit comments

Comments
 (0)