mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
GH-120804: Remove get_child_watcher
and set_child_watcher
from asyncio (#120818)
This commit is contained in:
parent
4717aaa1a7
commit
96ead91f0f
6 changed files with 18 additions and 227 deletions
|
@ -10,7 +10,6 @@ __all__ = (
|
||||||
'Handle', 'TimerHandle',
|
'Handle', 'TimerHandle',
|
||||||
'get_event_loop_policy', 'set_event_loop_policy',
|
'get_event_loop_policy', 'set_event_loop_policy',
|
||||||
'get_event_loop', 'set_event_loop', 'new_event_loop',
|
'get_event_loop', 'set_event_loop', 'new_event_loop',
|
||||||
'get_child_watcher', 'set_child_watcher',
|
|
||||||
'_set_running_loop', 'get_running_loop',
|
'_set_running_loop', 'get_running_loop',
|
||||||
'_get_running_loop',
|
'_get_running_loop',
|
||||||
)
|
)
|
||||||
|
@ -652,17 +651,6 @@ class AbstractEventLoopPolicy:
|
||||||
the current context, set_event_loop must be called explicitly."""
|
the current context, set_event_loop must be called explicitly."""
|
||||||
raise NotImplementedError
|
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):
|
class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
|
||||||
"""Default policy implementation for accessing the event loop.
|
"""Default policy implementation for accessing the event loop.
|
||||||
|
|
||||||
|
@ -837,17 +825,6 @@ def new_event_loop():
|
||||||
return get_event_loop_policy().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.
|
# Alias pure-Python implementations for testing purposes.
|
||||||
_py__get_running_loop = _get_running_loop
|
_py__get_running_loop = _get_running_loop
|
||||||
_py__set_running_loop = _set_running_loop
|
_py__set_running_loop = _set_running_loop
|
||||||
|
|
|
@ -199,7 +199,7 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
|
||||||
extra=None, **kwargs):
|
extra=None, **kwargs):
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.simplefilter('ignore', DeprecationWarning)
|
warnings.simplefilter('ignore', DeprecationWarning)
|
||||||
watcher = events.get_child_watcher()
|
watcher = events.get_event_loop_policy()._watcher
|
||||||
|
|
||||||
with watcher:
|
with watcher:
|
||||||
if not watcher.is_active():
|
if not watcher.is_active():
|
||||||
|
@ -1009,59 +1009,6 @@ class PidfdChildWatcher(AbstractChildWatcher):
|
||||||
return True
|
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):
|
class ThreadedChildWatcher(AbstractChildWatcher):
|
||||||
"""Threaded child watcher implementation.
|
"""Threaded child watcher implementation.
|
||||||
|
|
||||||
|
@ -1161,11 +1108,6 @@ class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._watcher = None
|
|
||||||
|
|
||||||
def _init_watcher(self):
|
|
||||||
with events._lock:
|
|
||||||
if self._watcher is None: # pragma: no branch
|
|
||||||
if can_use_pidfd():
|
if can_use_pidfd():
|
||||||
self._watcher = PidfdChildWatcher()
|
self._watcher = PidfdChildWatcher()
|
||||||
else:
|
else:
|
||||||
|
@ -1185,33 +1127,6 @@ class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
|
||||||
threading.current_thread() is threading.main_thread()):
|
threading.current_thread() is threading.main_thread()):
|
||||||
self._watcher.attach_loop(loop)
|
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
|
SelectorEventLoop = _UnixSelectorEventLoop
|
||||||
DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
|
DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
|
||||||
EventLoop = SelectorEventLoop
|
EventLoop = SelectorEventLoop
|
||||||
|
|
|
@ -2212,16 +2212,14 @@ 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.ThreadedChildWatcher()
|
watcher = asyncio.ThreadedChildWatcher()
|
||||||
watcher.attach_loop(self.loop)
|
watcher.attach_loop(self.loop)
|
||||||
asyncio.set_child_watcher(watcher)
|
policy = asyncio.get_event_loop_policy()
|
||||||
|
policy._watcher = watcher
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
with warnings.catch_warnings():
|
policy = asyncio.get_event_loop_policy()
|
||||||
warnings.simplefilter('ignore', DeprecationWarning)
|
policy._watcher = None
|
||||||
asyncio.set_child_watcher(None)
|
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
|
|
||||||
|
|
||||||
|
@ -2716,9 +2714,6 @@ class PolicyTests(unittest.TestCase):
|
||||||
self.assertRaises(NotImplementedError, policy.get_event_loop)
|
self.assertRaises(NotImplementedError, policy.get_event_loop)
|
||||||
self.assertRaises(NotImplementedError, policy.set_event_loop, object())
|
self.assertRaises(NotImplementedError, policy.set_event_loop, object())
|
||||||
self.assertRaises(NotImplementedError, policy.new_event_loop)
|
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):
|
def test_get_event_loop(self):
|
||||||
policy = asyncio.DefaultEventLoopPolicy()
|
policy = asyncio.DefaultEventLoopPolicy()
|
||||||
|
@ -2836,9 +2831,8 @@ class GetEventLoopTestsMixin:
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
try:
|
try:
|
||||||
if sys.platform != 'win32':
|
if sys.platform != 'win32':
|
||||||
with warnings.catch_warnings():
|
policy = asyncio.get_event_loop_policy()
|
||||||
warnings.simplefilter('ignore', DeprecationWarning)
|
policy._watcher = None
|
||||||
asyncio.set_child_watcher(None)
|
|
||||||
|
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -879,17 +879,13 @@ if sys.platform != 'win32':
|
||||||
|
|
||||||
watcher = self._get_watcher()
|
watcher = self._get_watcher()
|
||||||
watcher.attach_loop(self.loop)
|
watcher.attach_loop(self.loop)
|
||||||
with warnings.catch_warnings():
|
policy._watcher = watcher
|
||||||
warnings.simplefilter('ignore', DeprecationWarning)
|
|
||||||
policy.set_child_watcher(watcher)
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
policy = asyncio.get_event_loop_policy()
|
policy = asyncio.get_event_loop_policy()
|
||||||
with warnings.catch_warnings():
|
watcher = policy._watcher
|
||||||
warnings.simplefilter('ignore', DeprecationWarning)
|
policy._watcher = None
|
||||||
watcher = policy.get_child_watcher()
|
|
||||||
policy.set_child_watcher(None)
|
|
||||||
watcher.attach_loop(None)
|
watcher.attach_loop(None)
|
||||||
watcher.close()
|
watcher.close()
|
||||||
|
|
||||||
|
@ -910,66 +906,6 @@ if sys.platform != 'win32':
|
||||||
return unix_events.PidfdChildWatcher()
|
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:
|
else:
|
||||||
# Windows
|
# Windows
|
||||||
class SubprocessProactorTests(SubprocessMixin, test_utils.TestCase):
|
class SubprocessProactorTests(SubprocessMixin, test_utils.TestCase):
|
||||||
|
|
|
@ -1138,35 +1138,6 @@ class AbstractChildWatcherTests(unittest.TestCase):
|
||||||
NotImplementedError, watcher.__exit__, f, f, f)
|
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):
|
class TestFunctional(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -550,10 +550,8 @@ class TestCase(unittest.TestCase):
|
||||||
policy = support.maybe_get_event_loop_policy()
|
policy = support.maybe_get_event_loop_policy()
|
||||||
if policy is not None:
|
if policy is not None:
|
||||||
try:
|
try:
|
||||||
with warnings.catch_warnings():
|
watcher = policy._watcher
|
||||||
warnings.simplefilter('ignore', DeprecationWarning)
|
except AttributeError:
|
||||||
watcher = policy.get_child_watcher()
|
|
||||||
except NotImplementedError:
|
|
||||||
# watcher is not implemented by EventLoopPolicy, e.g. Windows
|
# watcher is not implemented by EventLoopPolicy, e.g. Windows
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue