changeset: 104301:83cc47533e4e parent: 104298:bcee710c42fe parent: 104300:fb6b60955d62 user: Yury Selivanov date: Wed Oct 05 17:02:07 2016 -0400 files: Misc/NEWS description: Merge 3.6 (issue #28368) diff -r bcee710c42fe -r 83cc47533e4e Lib/asyncio/unix_events.py --- a/Lib/asyncio/unix_events.py Wed Oct 05 23:18:32 2016 +0300 +++ b/Lib/asyncio/unix_events.py Wed Oct 05 17:02:07 2016 -0400 @@ -748,6 +748,7 @@ def __init__(self): self._loop = None + self._callbacks = {} def close(self): self.attach_loop(None) @@ -761,6 +762,12 @@ 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) @@ -809,10 +816,6 @@ big number of children (O(n) each time SIGCHLD is raised) """ - def __init__(self): - super().__init__() - self._callbacks = {} - def close(self): self._callbacks.clear() super().close() @@ -824,6 +827,11 @@ pass def add_child_handler(self, pid, callback, *args): + if self._loop is None: + raise RuntimeError( + "Cannot add child handler, " + "the child watcher does not have a loop attached") + self._callbacks[pid] = (callback, args) # Prevent a race condition in case the child is already terminated. @@ -888,7 +896,6 @@ """ def __init__(self): super().__init__() - self._callbacks = {} self._lock = threading.Lock() self._zombies = {} self._forks = 0 @@ -920,6 +927,12 @@ def add_child_handler(self, pid, callback, *args): assert self._forks, "Must use the context manager" + + if self._loop is None: + raise RuntimeError( + "Cannot add child handler, " + "the child watcher does not have a loop attached") + with self._lock: try: returncode = self._zombies.pop(pid) diff -r bcee710c42fe -r 83cc47533e4e Lib/test/test_asyncio/test_subprocess.py --- a/Lib/test/test_asyncio/test_subprocess.py Wed Oct 05 23:18:32 2016 +0300 +++ b/Lib/test/test_asyncio/test_subprocess.py Wed Oct 05 17:02:07 2016 -0400 @@ -433,6 +433,13 @@ # the transport was not notified yet self.assertFalse(killed) + # Unlike SafeChildWatcher, FastChildWatcher does not pop the + # callbacks if waitpid() is called elsewhere. Let's clear them + # manually to avoid a warning when the watcher is detached. + if sys.platform != 'win32' and \ + isinstance(self, SubprocessFastWatcherTests): + asyncio.get_child_watcher()._callbacks.clear() + def test_popen_error(self): # Issue #24763: check that the subprocess transport is closed # when BaseSubprocessTransport fails diff -r bcee710c42fe -r 83cc47533e4e Lib/test/test_asyncio/test_unix_events.py --- a/Lib/test/test_asyncio/test_unix_events.py Wed Oct 05 23:18:32 2016 +0300 +++ b/Lib/test/test_asyncio/test_unix_events.py Wed Oct 05 17:02:07 2016 -0400 @@ -11,6 +11,7 @@ import tempfile import threading import unittest +import warnings from unittest import mock if sys.platform == 'win32': @@ -1391,7 +1392,9 @@ with mock.patch.object( old_loop, "remove_signal_handler") as m_remove_signal_handler: - self.watcher.attach_loop(None) + with self.assertWarnsRegex( + RuntimeWarning, 'A loop is being detached'): + self.watcher.attach_loop(None) m_remove_signal_handler.assert_called_once_with( signal.SIGCHLD) @@ -1463,6 +1466,15 @@ if isinstance(self.watcher, asyncio.FastChildWatcher): self.assertFalse(self.watcher._zombies) + @waitpid_mocks + def test_add_child_handler_with_no_loop_attached(self, m): + callback = mock.Mock() + with self.create_watcher() as watcher: + with self.assertRaisesRegex( + RuntimeError, + 'the child watcher does not have a loop attached'): + watcher.add_child_handler(100, callback) + class SafeChildWatcherTests (ChildWatcherTestsMixin, test_utils.TestCase): def create_watcher(self): diff -r bcee710c42fe -r 83cc47533e4e Misc/NEWS --- a/Misc/NEWS Wed Oct 05 23:18:32 2016 +0300 +++ b/Misc/NEWS Wed Oct 05 17:02:07 2016 -0400 @@ -174,6 +174,10 @@ - Issue #28325: Remove vestigal MacOS 9 macurl2path module and its tests. +- Issue #28368: Refuse monitoring processes if the child watcher has + no loop attached. + Patch by Vincent Michel. + Windows -------