From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id 46BBE1382C5 for ; Sun, 6 May 2018 22:54:06 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 4FB31E088A; Sun, 6 May 2018 22:54:05 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 1BFA6E088A for ; Sun, 6 May 2018 22:54:03 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 3A736335C92 for ; Sun, 6 May 2018 22:54:02 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id 9669333 for ; Sun, 6 May 2018 22:54:00 +0000 (UTC) From: "Zac Medico" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Zac Medico" Message-ID: <1525647083.2d500ce2bc96995752dfc2fb475a7abe907e38b6.zmedico@gentoo> Subject: [gentoo-commits] proj/portage:master commit in: bin/, pym/portage/tests/, pym/portage/util/_eventloop/, ... X-VCS-Repository: proj/portage X-VCS-Files: bin/ebuild bin/ebuild-ipc.py bin/egencache bin/emaint bin/emerge bin/emirrordist bin/portageq bin/quickpkg pym/portage/tests/runTests.py pym/portage/tests/util/futures/asyncio/test_child_watcher.py pym/portage/tests/util/futures/asyncio/test_event_loop_in_fork.py pym/portage/tests/util/futures/asyncio/test_pipe_closed.py pym/portage/tests/util/futures/asyncio/test_run_until_complete.py pym/portage/tests/util/futures/asyncio/test_subprocess_exec.py pym/portage/tests/util/futures/test_retry.py pym/portage/util/_eventloop/global_event_loop.py X-VCS-Directories: pym/portage/tests/ bin/ pym/portage/tests/util/futures/asyncio/ pym/portage/tests/util/futures/ pym/portage/util/_eventloop/ X-VCS-Committer: zmedico X-VCS-Committer-Name: Zac Medico X-VCS-Revision: 2d500ce2bc96995752dfc2fb475a7abe907e38b6 X-VCS-Branch: master Date: Sun, 6 May 2018 22:54:00 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: b394a0e8-c868-4932-bd80-23074d0ac164 X-Archives-Hash: 12d7b21d8849a017d6e1e7c3e3a67635 commit: 2d500ce2bc96995752dfc2fb475a7abe907e38b6 Author: Zac Medico gentoo org> AuthorDate: Sun May 6 21:28:25 2018 +0000 Commit: Zac Medico gentoo org> CommitDate: Sun May 6 22:51:23 2018 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=2d500ce2 asyncio: explicitly close event loops (bug 654390) The default asyncio event loop triggers a resource warning if it is not explicitly closed, therefore close it when appropriate. Bug: https://bugs.gentoo.org/654390 bin/ebuild | 4 ++++ bin/ebuild-ipc.py | 5 ++++- bin/egencache | 5 ++++- bin/emaint | 3 +++ bin/emerge | 5 +++++ bin/emirrordist | 6 +++++- bin/portageq | 6 +++++- bin/quickpkg | 3 +++ pym/portage/tests/runTests.py | 6 +++++- pym/portage/tests/util/futures/asyncio/test_child_watcher.py | 5 +++++ pym/portage/tests/util/futures/asyncio/test_event_loop_in_fork.py | 6 ++++++ pym/portage/tests/util/futures/asyncio/test_pipe_closed.py | 7 +++++++ pym/portage/tests/util/futures/asyncio/test_run_until_complete.py | 5 +++++ pym/portage/tests/util/futures/asyncio/test_subprocess_exec.py | 7 ++++++- pym/portage/tests/util/futures/test_retry.py | 5 ++++- pym/portage/util/_eventloop/global_event_loop.py | 5 ++++- 16 files changed, 75 insertions(+), 8 deletions(-) diff --git a/bin/ebuild b/bin/ebuild index 5221b21a8..710257549 100755 --- a/bin/ebuild +++ b/bin/ebuild @@ -55,6 +55,7 @@ from portage.exception import PermissionDenied, PortageKeyError, \ PortagePackageException, UnsupportedAPIException from portage.localization import _ import portage.util +from portage.util._eventloop.global_event_loop import global_event_loop from _emerge.Package import Package from _emerge.RootConfig import RootConfig @@ -371,4 +372,7 @@ for arg in pargs: print("Could not run the required binary?") a = 127 if a: + global_event_loop().close() sys.exit(a) + +global_event_loop().close() diff --git a/bin/ebuild-ipc.py b/bin/ebuild-ipc.py index 6d0cdbef9..1f323bdc5 100755 --- a/bin/ebuild-ipc.py +++ b/bin/ebuild-ipc.py @@ -273,4 +273,7 @@ def ebuild_ipc_main(args): return ebuild_ipc.communicate(args) if __name__ == '__main__': - sys.exit(ebuild_ipc_main(sys.argv[1:])) + try: + sys.exit(ebuild_ipc_main(sys.argv[1:])) + finally: + global_event_loop().close() diff --git a/bin/egencache b/bin/egencache index e994b4ab1..70fb5aa00 100755 --- a/bin/egencache +++ b/bin/egencache @@ -1116,4 +1116,7 @@ def egencache_main(args): if __name__ == "__main__": portage._disable_legacy_globals() portage.util.noiselimit = -1 - sys.exit(egencache_main(sys.argv[1:])) + try: + sys.exit(egencache_main(sys.argv[1:])) + finally: + global_event_loop().close() diff --git a/bin/emaint b/bin/emaint index 08e75851a..a26dae1e7 100755 --- a/bin/emaint +++ b/bin/emaint @@ -31,6 +31,7 @@ if osp.isfile(osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), ".porta import portage portage._internal_caller = True from portage.emaint.main import emaint_main +from portage.util._eventloop.global_event_loop import global_event_loop try: emaint_main(sys.argv[1:]) @@ -40,3 +41,5 @@ except IOError as e: sys.exit(1) else: raise +finally: + global_event_loop().close() diff --git a/bin/emerge b/bin/emerge index 5f08861e5..e1262d544 100755 --- a/bin/emerge +++ b/bin/emerge @@ -13,6 +13,7 @@ import sys # exiting from signal handlers intermittently causes python to ignore # the SystemExit exception with a message like this: # Exception SystemExit: 130 in ignored +global_event_loop = None try: def exithandler(signum, _frame): @@ -41,6 +42,7 @@ try: import portage portage._internal_caller = True portage._disable_legacy_globals() + from portage.util._eventloop.global_event_loop import global_event_loop from _emerge.main import emerge_main if __name__ == "__main__": @@ -84,3 +86,6 @@ except KeyboardInterrupt: {"signal": signal.SIGINT}) sys.stderr.flush() sys.exit(128 + signal.SIGINT) +finally: + if global_event_loop is not None: + global_event_loop().close() diff --git a/bin/emirrordist b/bin/emirrordist index 17f99f590..3ea08379e 100755 --- a/bin/emirrordist +++ b/bin/emirrordist @@ -9,6 +9,7 @@ import portage portage._internal_caller = True portage._disable_legacy_globals() from portage._emirrordist.main import emirrordist_main +from portage.util._eventloop.global_event_loop import global_event_loop if __name__ == "__main__": @@ -18,4 +19,7 @@ if __name__ == "__main__": signal.signal(signal.SIGUSR1, debug_signal) - sys.exit(emirrordist_main(sys.argv[1:])) + try: + sys.exit(emirrordist_main(sys.argv[1:])) + finally: + global_event_loop().close() diff --git a/bin/portageq b/bin/portageq index e9b8b20e0..35499afd2 100755 --- a/bin/portageq +++ b/bin/portageq @@ -53,6 +53,7 @@ portage.proxy.lazyimport.lazyimport(globals(), '_emerge.is_valid_package_atom:insert_category_into_atom', 'portage.dbapi._expand_new_virt:expand_new_virt', 'portage._sets.base:InternalPackageSet', + 'portage.util._eventloop.global_event_loop:global_event_loop', 'portage.xml.metadata:MetaDataXML' ) @@ -1466,6 +1467,9 @@ def main(argv): sys.exit(1) if __name__ == '__main__': - sys.exit(main(sys.argv)) + try: + sys.exit(main(sys.argv)) + finally: + global_event_loop().close() #----------------------------------------------------------------------------- diff --git a/bin/quickpkg b/bin/quickpkg index 9765ec717..f071dd904 100755 --- a/bin/quickpkg +++ b/bin/quickpkg @@ -30,6 +30,8 @@ from portage.checksum import perform_md5 from portage._sets import load_default_config, SETPREFIX from portage.process import find_binary from portage.util.compression_probe import _compressors +from portage.util._eventloop.global_event_loop import global_event_loop + def quickpkg_atom(options, infos, arg, eout): settings = portage.settings @@ -390,4 +392,5 @@ if __name__ == "__main__": finally: os.umask(old_umask) signal.signal(signal.SIGWINCH, signal.SIG_DFL) + global_event_loop().close() sys.exit(retval) diff --git a/pym/portage/tests/runTests.py b/pym/portage/tests/runTests.py index 9c452764f..d4d1f7c76 100755 --- a/pym/portage/tests/runTests.py +++ b/pym/portage/tests/runTests.py @@ -42,6 +42,7 @@ if os.environ.get('NOCOLOR') in ('yes', 'true'): portage.output.nocolor() import portage.tests as tests +from portage.util._eventloop.global_event_loop import global_event_loop from portage.const import PORTAGE_BIN_PATH path = os.environ.get("PATH", "").split(":") path = [x for x in path if x] @@ -58,4 +59,7 @@ if insert_bin_path: os.environ["PATH"] = ":".join(path) if __name__ == "__main__": - sys.exit(tests.main()) + try: + sys.exit(tests.main()) + finally: + global_event_loop().close() diff --git a/pym/portage/tests/util/futures/asyncio/test_child_watcher.py b/pym/portage/tests/util/futures/asyncio/test_child_watcher.py index 8ef497544..0fc73ab49 100644 --- a/pym/portage/tests/util/futures/asyncio/test_child_watcher.py +++ b/pym/portage/tests/util/futures/asyncio/test_child_watcher.py @@ -5,6 +5,7 @@ import os from portage.process import find_binary, spawn from portage.tests import TestCase +from portage.util._eventloop.global_event_loop import global_event_loop from portage.util.futures import asyncio from portage.util.futures.unix_events import DefaultEventLoopPolicy @@ -18,6 +19,7 @@ class ChildWatcherTestCase(TestCase): if not isinstance(initial_policy, DefaultEventLoopPolicy): asyncio.set_event_loop_policy(DefaultEventLoopPolicy()) + loop = None try: try: asyncio.set_child_watcher(None) @@ -43,3 +45,6 @@ class ChildWatcherTestCase(TestCase): (pids[0], os.EX_OK, args_tuple)) finally: asyncio.set_event_loop_policy(initial_policy) + if loop not in (None, global_event_loop()): + loop.close() + self.assertFalse(global_event_loop().is_closed()) diff --git a/pym/portage/tests/util/futures/asyncio/test_event_loop_in_fork.py b/pym/portage/tests/util/futures/asyncio/test_event_loop_in_fork.py index 19588bf3a..177953437 100644 --- a/pym/portage/tests/util/futures/asyncio/test_event_loop_in_fork.py +++ b/pym/portage/tests/util/futures/asyncio/test_event_loop_in_fork.py @@ -5,6 +5,7 @@ import multiprocessing import os from portage.tests import TestCase +from portage.util._eventloop.global_event_loop import global_event_loop from portage.util.futures import asyncio from portage.util.futures.unix_events import DefaultEventLoopPolicy @@ -15,6 +16,7 @@ def fork_main(parent_conn, child_conn): # This fails with python's default event loop policy, # see https://bugs.python.org/issue22087. loop.run_until_complete(asyncio.sleep(0.1, loop=loop)) + loop.close() def async_main(fork_exitcode, loop=None): @@ -47,6 +49,7 @@ class EventLoopInForkTestCase(TestCase): initial_policy = asyncio.get_event_loop_policy() if not isinstance(initial_policy, DefaultEventLoopPolicy): asyncio.set_event_loop_policy(DefaultEventLoopPolicy()) + loop = None try: loop = asyncio._wrap_loop() fork_exitcode = loop.create_future() @@ -57,3 +60,6 @@ class EventLoopInForkTestCase(TestCase): assert loop.run_until_complete(fork_exitcode) == os.EX_OK finally: asyncio.set_event_loop_policy(initial_policy) + if loop not in (None, global_event_loop()): + loop.close() + self.assertFalse(global_event_loop().is_closed()) diff --git a/pym/portage/tests/util/futures/asyncio/test_pipe_closed.py b/pym/portage/tests/util/futures/asyncio/test_pipe_closed.py index c2b468064..507385c04 100644 --- a/pym/portage/tests/util/futures/asyncio/test_pipe_closed.py +++ b/pym/portage/tests/util/futures/asyncio/test_pipe_closed.py @@ -10,6 +10,7 @@ import sys import tempfile from portage.tests import TestCase +from portage.util._eventloop.global_event_loop import global_event_loop from portage.util.futures import asyncio from portage.util.futures.unix_events import ( DefaultEventLoopPolicy, @@ -83,6 +84,9 @@ class ReaderPipeClosedTestCase(_PipeClosedTestCase, TestCase): write_end.close() read_end.close() asyncio.set_event_loop_policy(initial_policy) + if loop not in (None, global_event_loop()): + loop.close() + self.assertFalse(global_event_loop().is_closed()) class WriterPipeClosedTestCase(_PipeClosedTestCase, TestCase): @@ -142,3 +146,6 @@ class WriterPipeClosedTestCase(_PipeClosedTestCase, TestCase): write_end.close() read_end.close() asyncio.set_event_loop_policy(initial_policy) + if loop not in (None, global_event_loop()): + loop.close() + self.assertFalse(global_event_loop().is_closed()) diff --git a/pym/portage/tests/util/futures/asyncio/test_run_until_complete.py b/pym/portage/tests/util/futures/asyncio/test_run_until_complete.py index 1a37e4922..c0e86ae5e 100644 --- a/pym/portage/tests/util/futures/asyncio/test_run_until_complete.py +++ b/pym/portage/tests/util/futures/asyncio/test_run_until_complete.py @@ -2,6 +2,7 @@ # Distributed under the terms of the GNU General Public License v2 from portage.tests import TestCase +from portage.util._eventloop.global_event_loop import global_event_loop from portage.util.futures import asyncio from portage.util.futures.unix_events import DefaultEventLoopPolicy @@ -12,6 +13,7 @@ class RunUntilCompleteTestCase(TestCase): if not isinstance(initial_policy, DefaultEventLoopPolicy): asyncio.set_event_loop_policy(DefaultEventLoopPolicy()) + loop = None try: loop = asyncio._wrap_loop() f1 = loop.create_future() @@ -27,3 +29,6 @@ class RunUntilCompleteTestCase(TestCase): self.assertEqual(f2.done(), True) finally: asyncio.set_event_loop_policy(initial_policy) + if loop not in (None, global_event_loop()): + loop.close() + self.assertFalse(global_event_loop().is_closed()) diff --git a/pym/portage/tests/util/futures/asyncio/test_subprocess_exec.py b/pym/portage/tests/util/futures/asyncio/test_subprocess_exec.py index 8dc5fa7b9..534d79c53 100644 --- a/pym/portage/tests/util/futures/asyncio/test_subprocess_exec.py +++ b/pym/portage/tests/util/futures/asyncio/test_subprocess_exec.py @@ -6,6 +6,7 @@ import subprocess from portage.process import find_binary from portage.tests import TestCase +from portage.util._eventloop.global_event_loop import global_event_loop from portage.util.futures import asyncio from portage.util.futures.executor.fork import ForkExecutor from portage.util.futures.unix_events import DefaultEventLoopPolicy @@ -60,10 +61,14 @@ class SubprocessExecTestCase(TestCase): if not isinstance(initial_policy, DefaultEventLoopPolicy): asyncio.set_event_loop_policy(DefaultEventLoopPolicy()) + loop = asyncio._wrap_loop() try: - test(asyncio._wrap_loop()) + test(loop) finally: asyncio.set_event_loop_policy(initial_policy) + if loop not in (None, global_event_loop()): + loop.close() + self.assertFalse(global_event_loop().is_closed()) def testEcho(self): if not hasattr(asyncio, 'create_subprocess_exec'): diff --git a/pym/portage/tests/util/futures/test_retry.py b/pym/portage/tests/util/futures/test_retry.py index 781eac9a1..baf293d56 100644 --- a/pym/portage/tests/util/futures/test_retry.py +++ b/pym/portage/tests/util/futures/test_retry.py @@ -183,7 +183,10 @@ class RetryExecutorTestCase(RetryTestCase): return result.result() else: # child process - return loop.run_until_complete(coroutine_func()) + try: + return loop.run_until_complete(coroutine_func()) + finally: + loop.close() def execute_wrapper(): kill_switch = parent_loop.create_future() diff --git a/pym/portage/util/_eventloop/global_event_loop.py b/pym/portage/util/_eventloop/global_event_loop.py index e2c7d71ea..a3ee9248d 100644 --- a/pym/portage/util/_eventloop/global_event_loop.py +++ b/pym/portage/util/_eventloop/global_event_loop.py @@ -29,6 +29,9 @@ def global_event_loop(): if not constructor.supports_multiprocessing and pid != _MAIN_PID: constructor = _multiprocessing_constructor - instance = constructor() + # Use the _asyncio_wrapper attribute, so that unit tests can compare + # the reference to one retured from _wrap_loop(), since they should + # not close the loop if it refers to a global event loop. + instance = constructor()._asyncio_wrapper _instances[pid] = instance return instance