From e49365f017813c5d8e8ffa6ef63a6b5c45030ffe Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 23 Jun 2022 17:51:28 +0100 Subject: [PATCH 01/19] run the PidfdChildWatcher on the running loop --- Lib/asyncio/unix_events.py | 44 +++++++++++--------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index cf7683fee64621..251c9b0b64acb9 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -911,10 +911,6 @@ class PidfdChildWatcher(AbstractChildWatcher): recent (5.3+) kernels. """ - def __init__(self): - self._loop = None - self._callbacks = {} - def __enter__(self): return self @@ -922,35 +918,22 @@ def __exit__(self, exc_type, exc_value, exc_traceback): pass def is_active(self): - return self._loop is not None and self._loop.is_running() + return True def close(self): - self.attach_loop(None) + pass def attach_loop(self, loop): - 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) - for pidfd, _, _ in self._callbacks.values(): - self._loop._remove_reader(pidfd) - os.close(pidfd) - self._callbacks.clear() - self._loop = loop + pass def add_child_handler(self, pid, callback, *args): - existing = self._callbacks.get(pid) - if existing is not None: - self._callbacks[pid] = existing[0], callback, args - else: - pidfd = os.pidfd_open(pid) - self._loop._add_reader(pidfd, self._do_wait, pid) - self._callbacks[pid] = pidfd, callback, args + loop = events.get_running_loop() + pidfd = os.pidfd_open(pid) + loop._add_reader(pidfd, self._do_wait, pid, pidfd, callback, args) - def _do_wait(self, pid): - pidfd, callback, args = self._callbacks.pop(pid) - self._loop._remove_reader(pidfd) + def _do_wait(self, pid, pidfd, callback, args): + loop = events.get_running_loop() + loop._remove_reader(pidfd) try: _, status = os.waitpid(pid, 0) except ChildProcessError: @@ -968,12 +951,9 @@ def _do_wait(self, pid): callback(pid, returncode, *args) def remove_child_handler(self, pid): - try: - pidfd, _, _ = self._callbacks.pop(pid) - except KeyError: - return False - self._loop._remove_reader(pidfd) - os.close(pidfd) + # asyncio never calls remove_child_handler() !!! + # The method is no-op but is implemented because + # abstract base classes require it. return True From 49cfa81f7962f64df20232829ea3ad8a8f93eb43 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 23 Jun 2022 18:23:27 +0100 Subject: [PATCH 02/19] enable GenericWatcherTests and add a test for pidfd watcher run in a thread --- Lib/test/test_asyncio/test_subprocess.py | 37 ++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 09a5c390b36299..c4bf1a46a19e40 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -712,7 +712,7 @@ def setUp(self): self.set_event_loop(self.loop) -class GenericWatcherTests: +class GenericWatcherTests(test_utils.TestCase): def test_create_subprocess_fails_with_inactive_watcher(self): @@ -727,9 +727,42 @@ async def execute(): watcher.add_child_handler.assert_not_called() - self.assertIsNone(self.loop.run_until_complete(execute())) + self.assertIsNone(asyncio.run(execute())) + + + 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 + + @unittest.skipUnless( + has_pidfd_support(), + "operating system does not support pidfds", + ) + def test_create_subprocess_with_pidfd(self): + async def in_thread(): + proc = await asyncio.create_subprocess_exec( + *args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + stdout, stderr = await proc.communicate(b"some data") + return proc.returncode, stdout + async def main(): + return await asyncio.to_thread(asyncio.run, in_thread()) + asyncio.set_child_watcher(PidfdChildWatcher()) + try: + returncode, stdout = asyncio.run(main()) + self.assertEqual(returncode, 0) + self.assertEqual(stdout, b'some data') + finally: + asyncio.set_child_watcher(None) if __name__ == '__main__': From 3a3d5e2ccb508381fe6aa94403ca74b33b29a869 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 23 Jun 2022 20:48:22 +0100 Subject: [PATCH 03/19] fix authspec typo --- Lib/test/test_asyncio/test_subprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index c4bf1a46a19e40..62d90b9b19c780 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -717,7 +717,7 @@ class GenericWatcherTests(test_utils.TestCase): def test_create_subprocess_fails_with_inactive_watcher(self): async def execute(): - watcher = mock.create_authspec(asyncio.AbstractChildWatcher) + watcher = mock.create_autopec(asyncio.AbstractChildWatcher) watcher.is_active.return_value = False asyncio.set_child_watcher(watcher) From 60dafa1e82cee29cb4918b77451633014bee599b Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 23 Jun 2022 20:49:10 +0100 Subject: [PATCH 04/19] Update Lib/test/test_asyncio/test_subprocess.py --- Lib/test/test_asyncio/test_subprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 62d90b9b19c780..19712d28fe0125 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -756,7 +756,7 @@ async def in_thread(): async def main(): return await asyncio.to_thread(asyncio.run, in_thread()) - asyncio.set_child_watcher(PidfdChildWatcher()) + asyncio.set_child_watcher(asyncio.PidfdChildWatcher()) try: returncode, stdout = asyncio.run(main()) self.assertEqual(returncode, 0) From 16f1c4ea2cf8a59972e7e834538dee3a29e301a3 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 23 Jun 2022 21:50:40 +0100 Subject: [PATCH 05/19] Update Lib/test/test_asyncio/test_subprocess.py --- Lib/test/test_asyncio/test_subprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 19712d28fe0125..6d5ade925b1712 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -746,7 +746,7 @@ def has_pidfd_support(): def test_create_subprocess_with_pidfd(self): async def in_thread(): proc = await asyncio.create_subprocess_exec( - *args, + *PROGRAM_CAT, stdin=subprocess.PIPE, stdout=subprocess.PIPE, ) From c8ae89d6652992df40de84eb9200881826ebafbe Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 23 Jun 2022 22:35:23 +0100 Subject: [PATCH 06/19] Update Lib/test/test_asyncio/test_subprocess.py --- Lib/test/test_asyncio/test_subprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 6d5ade925b1712..b857008cee874a 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -717,7 +717,7 @@ class GenericWatcherTests(test_utils.TestCase): def test_create_subprocess_fails_with_inactive_watcher(self): async def execute(): - watcher = mock.create_autopec(asyncio.AbstractChildWatcher) + watcher = mock.create_autospec(asyncio.AbstractChildWatcher) watcher.is_active.return_value = False asyncio.set_child_watcher(watcher) From 8229efc051ada02356bb3f6ee1a24031cc150aa8 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 24 Jun 2022 00:21:52 +0100 Subject: [PATCH 07/19] Update Lib/test/test_asyncio/test_subprocess.py --- Lib/test/test_asyncio/test_subprocess.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index b857008cee874a..562873741de3fb 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -720,6 +720,8 @@ async def execute(): watcher = mock.create_autospec(asyncio.AbstractChildWatcher) watcher.is_active.return_value = False asyncio.set_child_watcher(watcher) + self.assertIs(asyncio.get_child_watcher(), watcher) + self.assertFalse(asyncio.get_child_watcher().is_active()) with self.assertRaises(RuntimeError): await subprocess.create_subprocess_exec( From 02190f2c7ae1bf9a036e1e8a9c0c959c2ef56ce2 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 24 Jun 2022 08:52:43 +0100 Subject: [PATCH 08/19] AbstractChildWatcher is called as a context manager test_create_subprocess_fails_with_inactive_watcher was only setting the is_active() method on the context manager not the context manager result --- Lib/test/test_asyncio/test_subprocess.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 562873741de3fb..834d7d21da6f96 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -715,13 +715,13 @@ def setUp(self): class GenericWatcherTests(test_utils.TestCase): def test_create_subprocess_fails_with_inactive_watcher(self): + watcher = mock.create_autospec( + asyncio.AbstractChildWatcher, + **{"__enter__.return_value.is_active.return_value": False} + ) async def execute(): - watcher = mock.create_autospec(asyncio.AbstractChildWatcher) - watcher.is_active.return_value = False asyncio.set_child_watcher(watcher) - self.assertIs(asyncio.get_child_watcher(), watcher) - self.assertFalse(asyncio.get_child_watcher().is_active()) with self.assertRaises(RuntimeError): await subprocess.create_subprocess_exec( @@ -730,6 +730,11 @@ async def execute(): watcher.add_child_handler.assert_not_called() self.assertIsNone(asyncio.run(execute())) + self.assertListEqual(watcher.mock_calls, [ + mock.call.__enter__(), + mock.call.__enter__().is_active(), + mock.call.__exit__(RuntimeError, mock.ANY, mock.ANY), + ]) def has_pidfd_support(): From 9607d8e09870a4e5731a4505c94b2b755fb9a7f9 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 24 Jun 2022 08:55:05 +0100 Subject: [PATCH 09/19] move GenericWatcherTests into the other unix tests --- Lib/test/test_asyncio/test_subprocess.py | 114 +++++++++++------------ 1 file changed, 56 insertions(+), 58 deletions(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 834d7d21da6f96..9e5b5aa506cb9e 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -702,75 +702,73 @@ class SubprocessPidfdWatcherTests(SubprocessWatcherMixin, test_utils.TestCase): Watcher = unix_events.PidfdChildWatcher -else: - # Windows - class SubprocessProactorTests(SubprocessMixin, test_utils.TestCase): - def setUp(self): - super().setUp() - self.loop = asyncio.ProactorEventLoop() - self.set_event_loop(self.loop) - - -class GenericWatcherTests(test_utils.TestCase): - - def test_create_subprocess_fails_with_inactive_watcher(self): - watcher = mock.create_autospec( - asyncio.AbstractChildWatcher, - **{"__enter__.return_value.is_active.return_value": False} - ) + class GenericWatcherTests(test_utils.TestCase): - async def execute(): - asyncio.set_child_watcher(watcher) + def test_create_subprocess_fails_with_inactive_watcher(self): + watcher = mock.create_autospec( + asyncio.AbstractChildWatcher, + **{"__enter__.return_value.is_active.return_value": False} + ) - with self.assertRaises(RuntimeError): - await subprocess.create_subprocess_exec( - os_helper.FakePath(sys.executable), '-c', 'pass') + async def execute(): + asyncio.set_child_watcher(watcher) - watcher.add_child_handler.assert_not_called() + with self.assertRaises(RuntimeError): + await subprocess.create_subprocess_exec( + os_helper.FakePath(sys.executable), '-c', 'pass') - self.assertIsNone(asyncio.run(execute())) - self.assertListEqual(watcher.mock_calls, [ - mock.call.__enter__(), - mock.call.__enter__().is_active(), - mock.call.__exit__(RuntimeError, mock.ANY, mock.ANY), - ]) + watcher.add_child_handler.assert_not_called() + self.assertIsNone(asyncio.run(execute())) + self.assertListEqual(watcher.mock_calls, [ + mock.call.__enter__(), + mock.call.__enter__().is_active(), + mock.call.__exit__(RuntimeError, mock.ANY, mock.ANY), + ]) - 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 - @unittest.skipUnless( - has_pidfd_support(), - "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 + 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 + + @unittest.skipUnless( + has_pidfd_support(), + "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(): - return await asyncio.to_thread(asyncio.run, in_thread()) + async def main(): + return await asyncio.to_thread(asyncio.run, in_thread()) - asyncio.set_child_watcher(asyncio.PidfdChildWatcher()) - try: - returncode, stdout = asyncio.run(main()) - self.assertEqual(returncode, 0) - self.assertEqual(stdout, b'some data') - finally: - asyncio.set_child_watcher(None) + asyncio.set_child_watcher(asyncio.PidfdChildWatcher()) + try: + returncode, stdout = asyncio.run(main()) + self.assertEqual(returncode, 0) + self.assertEqual(stdout, b'some data') + finally: + asyncio.set_child_watcher(None) +else: + # Windows + class SubprocessProactorTests(SubprocessMixin, test_utils.TestCase): + def setUp(self): + super().setUp() + self.loop = asyncio.ProactorEventLoop() + self.set_event_loop(self.loop) if __name__ == '__main__': unittest.main() From 6e561559789cc42243e2bc46b63c682f70ed6c54 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 24 Jun 2022 09:09:51 +0100 Subject: [PATCH 10/19] check that asyncio.set_event_loop() was not called otherwise it invalidates test_create_subprocess_with_pidfd --- Lib/test/test_asyncio/test_subprocess.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 9e5b5aa506cb9e..609542329408ed 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -752,6 +752,9 @@ async def in_thread(): return proc.returncode, stdout async def main(): + # asyncio.run did not call asyncio.set_event_loop() + with self.assertRaises(RuntimeError): + asyncio.get_event_loop_policy().get_event_loop() return await asyncio.to_thread(asyncio.run, in_thread()) asyncio.set_child_watcher(asyncio.PidfdChildWatcher()) From 1037078590c67d3a17ef2438501cb40e8406fd55 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 24 Jun 2022 08:49:49 +0000 Subject: [PATCH 11/19] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst diff --git a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst new file mode 100644 index 00000000000000..6b7db2c3ed00a8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst @@ -0,0 +1 @@ +run the PidfdChildWatcher on the running loop From 5647edb332c1793de7722729c1c867677f2f5ccc Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 19 Jul 2022 14:53:54 +0100 Subject: [PATCH 12/19] asyncio.run sets up the policy system asyncio.Runner with a loop_factory does not --- Lib/test/test_asyncio/test_subprocess.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index ca400057a058dd..f7ebbd19981835 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -718,7 +718,8 @@ async def execute(): watcher.add_child_handler.assert_not_called() - self.assertIsNone(asyncio.run(execute())) + with asyncio.Runner(loop_factory=asyncio.new_event_loop) as runner: + self.assertIsNone(runner.run(execute())) self.assertListEqual(watcher.mock_calls, [ mock.call.__enter__(), mock.call.__enter__().is_active(), @@ -750,14 +751,15 @@ async def in_thread(): return proc.returncode, stdout async def main(): - # asyncio.run did not call asyncio.set_event_loop() + # asyncio.Runner did not call asyncio.set_event_loop() with self.assertRaises(RuntimeError): asyncio.get_event_loop_policy().get_event_loop() return await asyncio.to_thread(asyncio.run, in_thread()) asyncio.set_child_watcher(asyncio.PidfdChildWatcher()) try: - returncode, stdout = asyncio.run(main()) + 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: From 102ba29b7ee141420208b3b75bb8c43d8cada0cf Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Tue, 19 Jul 2022 16:27:34 +0100 Subject: [PATCH 13/19] Update Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst --- .../next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst index 6b7db2c3ed00a8..526f014243f620 100644 --- a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst +++ b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst @@ -1 +1 @@ -run the PidfdChildWatcher on the running loop +run the :class:`asyncio.PidfdChildWatcher` on the running loop From de21df089d52e99731d353958dce2ef222672817 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 20 Jul 2022 09:13:26 +0100 Subject: [PATCH 14/19] Update Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst --- .../next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst index 526f014243f620..c7be8640ef1f7e 100644 --- a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst +++ b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst @@ -1 +1 @@ -run the :class:`asyncio.PidfdChildWatcher` on the running loop +run the :class:`asyncio.PidfdChildWatcher` on the running loop, this allows event loops to run subprocesses when there is no default event loop running on the main thread From d30824f527bd7861275b83dc0cdfb7c3401e7e23 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 20 Jul 2022 09:23:04 +0100 Subject: [PATCH 15/19] move duplicate has_pidfd_support to module global --- Lib/test/test_asyncio/test_subprocess.py | 36 +++++++++++------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index f7ebbd19981835..91b48e2e743c87 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -3,6 +3,7 @@ import sys import unittest import warnings +import functools from unittest import mock import asyncio @@ -29,6 +30,19 @@ 'sys.stdout.buffer.write(data)'))] +@functools.cached +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) @@ -683,17 +697,8 @@ class SubprocessFastWatcherTests(SubprocessWatcherMixin, Watcher = unix_events.FastChildWatcher - 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 - @unittest.skipUnless( - has_pidfd_support(), + _has_pidfd_support(), "operating system does not support pidfds", ) class SubprocessPidfdWatcherTests(SubprocessWatcherMixin, @@ -727,17 +732,8 @@ async def execute(): ]) - 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 - @unittest.skipUnless( - has_pidfd_support(), + _has_pidfd_support(), "operating system does not support pidfds", ) def test_create_subprocess_with_pidfd(self): From 978c822282fba0c722cbdcc9e2a1955a917e1c28 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 20 Jul 2022 11:29:08 +0100 Subject: [PATCH 16/19] Update Lib/test/test_asyncio/test_subprocess.py --- Lib/test/test_asyncio/test_subprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 91b48e2e743c87..d2f3a92ecb51dc 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -30,7 +30,7 @@ 'sys.stdout.buffer.write(data)'))] -@functools.cached +@functools.cache def _has_pidfd_support(): if not hasattr(os, 'pidfd_open'): return False From 6fe49854dcb53356891f20ec0f4b2dadd5e9aa2b Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 21 Jul 2022 14:26:25 +0100 Subject: [PATCH 17/19] Update Lib/test/test_asyncio/test_subprocess.py --- Lib/test/test_asyncio/test_subprocess.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index d2f3a92ecb51dc..b3ae7d194bd5ae 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -769,5 +769,6 @@ def setUp(self): self.loop = asyncio.ProactorEventLoop() self.set_event_loop(self.loop) + if __name__ == '__main__': unittest.main() From fc2e4000572385f94768de290a00683cebfc73a9 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 7 Oct 2022 13:27:43 -0700 Subject: [PATCH 18/19] Update Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst --- .../next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst index c7be8640ef1f7e..a6875520fd2242 100644 --- a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst +++ b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst @@ -1 +1 @@ -run the :class:`asyncio.PidfdChildWatcher` on the running loop, this allows event loops to run subprocesses when there is no default event loop running on the main thread +run the ``asyncio.PidfdChildWatcher`` on the running loop, this allows event loops to run subprocesses when there is no default event loop running on the main thread From 65a2db623cd0d2df8935acade4c774ff5e4ac396 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 7 Oct 2022 13:31:18 -0700 Subject: [PATCH 19/19] Update Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst --- .../next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst index a6875520fd2242..c7be8640ef1f7e 100644 --- a/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst +++ b/Misc/NEWS.d/next/Library/2022-06-24-08-49-47.gh-issue-94182.Wknau0.rst @@ -1 +1 @@ -run the ``asyncio.PidfdChildWatcher`` on the running loop, this allows event loops to run subprocesses when there is no default event loop running on the main thread +run the :class:`asyncio.PidfdChildWatcher` on the running loop, this allows event loops to run subprocesses when there is no default event loop running on the main thread