[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: d718cea94a180042b2285698b2c19113c5d25987 Author: Zac Medico gentoo org> AuthorDate: Thu Feb 22 06:41:49 2024 + Commit: Zac Medico gentoo org> CommitDate: Thu Feb 22 07:28:38 2024 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d718cea9 _get_running_loop: Support real asyncio.run When called via the real asyncio.run implementation, wrap the running asyncio loop. Otherwise, it's not possible to call portage libraries via the real asyncio.run without triggering Future "attached to a different loop" errors. Bug: https://bugs.gentoo.org/761538 Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 28 +-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index 22241f335d..4eecc46a89 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -325,13 +325,37 @@ def _safe_loop(): def _get_running_loop(): +""" +This calls the real asyncio get_running_loop() and wraps that with +portage's internal AsyncioEventLoop wrapper. If there is no running +asyncio event loop but portage has a reference to another running +loop in this thread, then use that instead. + +This behavior enables portage internals to use the real asyncio.run +while remaining compatible with internal code that does not use the +real asyncio.run. +""" +try: +_loop = _real_asyncio.get_running_loop() +except RuntimeError: +_loop = None + with _thread_weakrefs.lock: if _thread_weakrefs.pid == portage.getpid(): try: loop = _thread_weakrefs.loops[threading.get_ident()] except KeyError: -return None -return loop if loop.is_running() else None +pass +else: +if _loop is loop._loop: +return loop +elif _loop is None: +return loop if loop.is_running() else None + +# If _loop it not None here it means it was probably a temporary +# loop created by asyncio.run, so we don't try to cache it, and +# just return a temporary wrapper. +return None if _loop is None else _AsyncioEventLoop(loop=_loop) def _thread_weakrefs_atexit():
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/package/ebuild/, ...
commit: 18cdb6331a66c1cc92f296b1aaf0538f63586875 Author: Zac Medico gentoo org> AuthorDate: Tue Feb 13 08:44:50 2024 + Commit: Zac Medico gentoo org> CommitDate: Wed Feb 21 15:27:31 2024 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=18cdb633 EbuildPhase: async_check_locale Change config.environ() check_locale calls to async_check_locale calls in the EbuildPhase _async_start method in order to eliminate synchronous waiting for child processes in the main event loop thread. Bug: https://bugs.gentoo.org/923841 Signed-off-by: Zac Medico gentoo.org> lib/_emerge/EbuildMetadataPhase.py| 4 lib/_emerge/EbuildPhase.py| 28 ++- lib/portage/package/ebuild/config.py | 26 +++-- lib/portage/util/futures/_asyncio/__init__.py | 9 + lib/portage/util/locale.py| 28 ++- 5 files changed, 70 insertions(+), 25 deletions(-) diff --git a/lib/_emerge/EbuildMetadataPhase.py b/lib/_emerge/EbuildMetadataPhase.py index fc64d84c94..54177840c7 100644 --- a/lib/_emerge/EbuildMetadataPhase.py +++ b/lib/_emerge/EbuildMetadataPhase.py @@ -8,6 +8,7 @@ import portage portage.proxy.lazyimport.lazyimport( globals(), +"_emerge.EbuildPhase:_setup_locale", "portage.package.ebuild._metadata_invalid:eapi_invalid", ) from portage import os @@ -83,6 +84,9 @@ class EbuildMetadataPhase(SubProcess): settings.setcpv(self.cpv) settings.configdict["pkg"]["EAPI"] = parsed_eapi +# This requires above setcpv and EAPI setup. +await _setup_locale(self.settings) + debug = settings.get("PORTAGE_DEBUG") == "1" master_fd = None slave_fd = None diff --git a/lib/_emerge/EbuildPhase.py b/lib/_emerge/EbuildPhase.py index 3b366f39c7..b472803438 100644 --- a/lib/_emerge/EbuildPhase.py +++ b/lib/_emerge/EbuildPhase.py @@ -1,4 +1,4 @@ -# Copyright 1999-2021 Gentoo Authors +# Copyright 1999-2024 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import functools @@ -24,6 +24,7 @@ from portage.package.ebuild.prepare_build_dirs import ( _prepare_fake_distdir, _prepare_fake_filesdir, ) +from portage.eapi import _get_eapi_attrs from portage.util import writemsg, ensure_dirs from portage.util._async.AsyncTaskFuture import AsyncTaskFuture from portage.util._async.BuildLogger import BuildLogger @@ -54,12 +55,34 @@ portage.proxy.lazyimport.lazyimport( + "_post_src_install_write_metadata," + "_preinst_bsdflags", "portage.util.futures.unix_events:_set_nonblocking", +"portage.util.locale:async_check_locale,split_LC_ALL", ) from portage import os from portage import _encodings from portage import _unicode_encode +async def _setup_locale(settings): +eapi_attrs = _get_eapi_attrs(settings["EAPI"]) +if eapi_attrs.posixish_locale: +split_LC_ALL(settings) +settings["LC_COLLATE"] = "C" +# check_locale() returns None when check can not be executed. +if await async_check_locale(silent=True, env=settings.environ()) is False: +# try another locale +for l in ("C.UTF-8", "en_US.UTF-8", "en_GB.UTF-8", "C"): +settings["LC_CTYPE"] = l +if await async_check_locale(silent=True, env=settings.environ()): +# TODO: output the following only once +# writemsg( +# _("!!! LC_CTYPE unsupported, using %s instead\n") +# % self.settings["LC_CTYPE"] +# ) +break +else: +raise AssertionError("C locale did not pass the test!") + + class EbuildPhase(CompositeTask): __slots__ = ("actionmap", "fd_pipes", "phase", "settings") + ("_ebuild_lock",) @@ -95,6 +118,9 @@ class EbuildPhase(CompositeTask): self._start_task(AsyncTaskFuture(future=future), self._async_start_exit) async def _async_start(self): + +await _setup_locale(self.settings) + need_builddir = self.phase not in EbuildProcess._phases_without_builddir if need_builddir: diff --git a/lib/portage/package/ebuild/config.py b/lib/portage/package/ebuild/config.py index c89354cbf7..bafdc55a08 100644 --- a/lib/portage/package/ebuild/config.py +++ b/lib/portage/package/ebuild/config.py @@ -29,7 +29,6 @@ portage.proxy.lazyimport.lazyimport( "portage.dbapi.vartree:vartree", "portage.package.ebuild.doebuild:_phase_func_map", "portage.util.compression_probe:_compressors", -"portage.util.locale:check_locale,split_LC_ALL", ) from portage import bsd_chflags, load_mod, os, selinux, _unicode_decode from portage.const import ( @@ -3371,20 +3370,17 @@ class config: mydict["EBUILD_PHASE_FUNC"] = phase_func if eapi_attrs.posixish_locale: -split_LC_ALL(mydict) -
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: a42c2164ada634262ae1f791ad60298fe3468a94 Author: Zac Medico gentoo org> AuthorDate: Tue Feb 13 03:39:35 2024 + Commit: Zac Medico gentoo org> CommitDate: Wed Feb 21 15:27:31 2024 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=a42c2164 asyncio: Wrap asyncio.Lock for python 3.9 compat Wrap asyncio.Lock for compatibility with python 3.9 where the deprecated loop parameter is required in order to avoid "got Future attached to a different loop" errors. The pordbapi async_aux_get method can use asyncio.Lock to serialize access to its doebuild_settings attribute in order to prevent issues like bug 924319. Bug: https://bugs.gentoo.org/924319 Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 17 + 1 file changed, 17 insertions(+) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index 8f1b8e8275..b6481c281e 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -9,6 +9,7 @@ __all__ = ( "CancelledError", "Future", "InvalidStateError", +"Lock", "TimeoutError", "get_child_watcher", "get_event_loop", @@ -22,6 +23,7 @@ __all__ = ( "wait_for", ) +import sys import types import weakref @@ -35,6 +37,7 @@ from asyncio import ( FIRST_EXCEPTION, Future, InvalidStateError, +Lock as _Lock, shield, TimeoutError, wait_for, @@ -159,6 +162,20 @@ def iscoroutinefunction(func): return False +class Lock(_Lock): +""" +Inject loop parameter for python3.9 or less in order to avoid +"got Future attached to a different loop" errors. +""" + +def __init__(self, **kwargs): +if sys.version_info >= (3, 10): +kwargs.pop("loop", None) +elif "loop" not in kwargs: +kwargs["loop"] = _safe_loop()._loop +super().__init__(**kwargs) + + class Task(Future): """ Schedule the execution of a coroutine: wrap it in a future. A task
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/util/, ...
commit: c95fc64abf9698263090b3ffd4a056e989dd2be1 Author: Zac Medico gentoo org> AuthorDate: Fri Feb 9 06:38:41 2024 + Commit: Zac Medico gentoo org> CommitDate: Fri Feb 9 08:19:00 2024 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=c95fc64a EbuildPhase: async_check_locale Change config.environ() check_locale calls to async_check_locale calls in the EbuildPhase _async_start method in order to eliminate synchronous waiting for child processes in the main event loop thread. Bug: https://bugs.gentoo.org/923841 Signed-off-by: Zac Medico gentoo.org> lib/_emerge/EbuildMetadataPhase.py| 21 lib/_emerge/EbuildPhase.py| 28 ++- lib/portage/package/ebuild/config.py | 26 +++-- lib/portage/util/futures/_asyncio/__init__.py | 9 + lib/portage/util/locale.py| 28 ++- 5 files changed, 87 insertions(+), 25 deletions(-) diff --git a/lib/_emerge/EbuildMetadataPhase.py b/lib/_emerge/EbuildMetadataPhase.py index f4f685e81c..53b7ad9624 100644 --- a/lib/_emerge/EbuildMetadataPhase.py +++ b/lib/_emerge/EbuildMetadataPhase.py @@ -8,12 +8,14 @@ import portage portage.proxy.lazyimport.lazyimport( globals(), +"_emerge.EbuildPhase:_setup_locale", "portage.package.ebuild._metadata_invalid:eapi_invalid", ) from portage import os from portage import _encodings from portage import _unicode_decode from portage import _unicode_encode +from portage.util.futures import asyncio import fcntl @@ -44,6 +46,12 @@ class EbuildMetadataPhase(SubProcess): _files_dict = slot_dict_class(_file_names, prefix="") def _start(self): +asyncio.ensure_future( +self._async_start(), loop=self.scheduler +).add_done_callback(self._async_start_done) + +async def _async_start(self): + ebuild_path = self.ebuild_hash.location with open( @@ -75,6 +83,9 @@ class EbuildMetadataPhase(SubProcess): settings.setcpv(self.cpv) settings.configdict["pkg"]["EAPI"] = parsed_eapi +# This requires above setcpv and EAPI setup. +await _setup_locale(self.settings) + debug = settings.get("PORTAGE_DEBUG") == "1" master_fd = None slave_fd = None @@ -139,6 +150,16 @@ class EbuildMetadataPhase(SubProcess): self._proc = retval +def _async_start_done(self, future): +future.cancelled() or future.result() +if future.cancelled(): +self.cancel() +self._was_cancelled() + +if self.returncode is not None: +self._unregister() +self.wait() + def _output_handler(self): while True: buf = self._read_buf(self._files.ebuild) diff --git a/lib/_emerge/EbuildPhase.py b/lib/_emerge/EbuildPhase.py index c81bf54a81..c8caf73722 100644 --- a/lib/_emerge/EbuildPhase.py +++ b/lib/_emerge/EbuildPhase.py @@ -1,4 +1,4 @@ -# Copyright 1999-2021 Gentoo Authors +# Copyright 1999-2024 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import functools @@ -24,6 +24,7 @@ from portage.package.ebuild.prepare_build_dirs import ( _prepare_fake_distdir, _prepare_fake_filesdir, ) +from portage.eapi import _get_eapi_attrs from portage.util import writemsg, ensure_dirs from portage.util._async.AsyncTaskFuture import AsyncTaskFuture from portage.util._async.BuildLogger import BuildLogger @@ -54,12 +55,34 @@ portage.proxy.lazyimport.lazyimport( + "_post_src_install_write_metadata," + "_preinst_bsdflags", "portage.util.futures.unix_events:_set_nonblocking", +"portage.util.locale:async_check_locale,split_LC_ALL", ) from portage import os from portage import _encodings from portage import _unicode_encode +async def _setup_locale(settings): +eapi_attrs = _get_eapi_attrs(settings["EAPI"]) +if eapi_attrs.posixish_locale: +split_LC_ALL(settings) +settings["LC_COLLATE"] = "C" +# check_locale() returns None when check can not be executed. +if await async_check_locale(silent=True, env=settings.environ()) is False: +# try another locale +for l in ("C.UTF-8", "en_US.UTF-8", "en_GB.UTF-8", "C"): +settings["LC_CTYPE"] = l +if await async_check_locale(silent=True, env=settings.environ()): +# TODO: output the following only once +# writemsg( +# _("!!! LC_CTYPE unsupported, using %s instead\n") +# % self.settings["LC_CTYPE"] +# ) +break +else: +raise AssertionError("C locale did not pass the test!") + + class EbuildPhase(CompositeTask): __slots__ = ("actionmap", "fd_pipes", "phase", "settings") + ("_ebuild_lock",) @@ -94,6 +117,9 @@ class
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/sync/modules/rsync/
commit: 62332ee82b8b88fa5a65aafa7c221ccdaa7d65a8 Author: Zac Medico gentoo org> AuthorDate: Sun Feb 4 00:11:07 2024 + Commit: Zac Medico gentoo org> CommitDate: Wed Feb 7 00:55:46 2024 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=62332ee8 RsyncSync: Migrate to spawn returnproc parameter Bug: https://bugs.gentoo.org/916566 Signed-off-by: Zac Medico gentoo.org> lib/portage/sync/modules/rsync/rsync.py | 40 +-- lib/portage/util/futures/_asyncio/__init__.py | 6 +++- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/lib/portage/sync/modules/rsync/rsync.py b/lib/portage/sync/modules/rsync/rsync.py index 175c7f2e8e..5d442d2626 100644 --- a/lib/portage/sync/modules/rsync/rsync.py +++ b/lib/portage/sync/modules/rsync/rsync.py @@ -1,4 +1,4 @@ -# Copyright 1999-2023 Gentoo Authors +# Copyright 1999-2024 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import datetime @@ -708,48 +708,47 @@ class RsyncSync(NewBase): command.append(syncuri.rstrip("/") + "/metadata/timestamp.chk") command.append(tmpservertimestampfile) content = None -pids = [] +proc = None +proc_waiter = None +loop = asyncio.get_event_loop() try: # Timeout here in case the server is unresponsive. The # --timeout rsync option doesn't apply to the initial # connection attempt. try: -if self.rsync_initial_timeout: - portage.exception.AlarmSignal.register(self.rsync_initial_timeout) - -pids.extend( -portage.process.spawn(command, returnpid=True, **self.spawn_kwargs) +proc = portage.process.spawn( +command, returnproc=True, **self.spawn_kwargs +) +proc_waiter = asyncio.ensure_future(proc.wait(), loop) +future = ( +asyncio.wait_for( +asyncio.shield(proc_waiter), self.rsync_initial_timeout +) +if self.rsync_initial_timeout +else proc_waiter ) -exitcode = os.waitpid(pids[0], 0)[1] +exitcode = loop.run_until_complete(future) if self.usersync_uid is not None: portage.util.apply_permissions( tmpservertimestampfile, uid=os.getuid() ) content = portage.grabfile(tmpservertimestampfile) finally: -if self.rsync_initial_timeout: -portage.exception.AlarmSignal.unregister() try: os.unlink(tmpservertimestampfile) except OSError: pass -except portage.exception.AlarmSignal: +except (TimeoutError, asyncio.TimeoutError): # timed out print("timed out") # With waitpid and WNOHANG, only check the # first element of the tuple since the second # element may vary (bug #337465). -if pids and os.waitpid(pids[0], os.WNOHANG)[0] == 0: -os.kill(pids[0], signal.SIGTERM) -os.waitpid(pids[0], 0) +if proc_waiter and not proc_waiter.done(): +proc.terminate() +loop.run_until_complete(proc_waiter) # This is the same code rsync uses for timeout. exitcode = 30 -else: -if exitcode != os.EX_OK: -if exitcode & 0xFF: -exitcode = (exitcode & 0xFF) << 8 -else: -exitcode = exitcode >> 8 if content: try: @@ -758,7 +757,6 @@ class RsyncSync(NewBase): ) except (OverflowError, ValueError): pass -del command, pids, content if exitcode == os.EX_OK: if (servertimestamp != 0) and (servertimestamp == timestamp): diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index a5a6cb3a5b..8f1b8e8275 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2018-2021 Gentoo Authors +# Copyright 2018-2024 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 __all__ = ( @@ -15,9 +15,11 @@ __all__ = ( "set_child_watcher", "get_event_loop_policy", "set_event_loop_policy", +"shield", "sleep", "Task", "wait", +"wait_for", ) import types @@ -33,7 +35,9 @@ from asyncio import ( FIRST_EXCEPTION, Future, InvalidStateError, +shield, TimeoutError, +wait_for, ) import threading
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/tests/process/, lib/_emerge/, ...
commit: efbc9c8d38b742a37968fc3b33d3dffdaf3be9f2 Author: Berin Aniesh gmail com> AuthorDate: Fri Jun 9 02:00:45 2023 + Commit: Sam James gentoo org> CommitDate: Wed Jun 14 01:44:02 2023 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=efbc9c8d Remove dummy_threading Portage's MSV is python 3.7 and dummy threading is deprecated since python 3.7. So everything dummy_threading is removed. Signed-off-by: Berin Aniesh gmail.com> Closes: https://github.com/gentoo/portage/pull/1052 Signed-off-by: Sam James gentoo.org> lib/_emerge/AsynchronousLock.py| 29 lib/_emerge/PollScheduler.py | 7 ++--- lib/portage/_emirrordist/MirrorDistTask.py | 6 + lib/portage/debug.py | 8 ++ lib/portage/locks.py | 7 +++-- lib/portage/proxy/lazyimport.py| 8 ++ lib/portage/tests/locks/test_asynchronous_lock.py | 31 -- .../tests/process/test_PopenProcessBlockingIO.py | 14 +- lib/portage/tests/util/futures/test_retry.py | 7 ++--- lib/portage/util/_async/PipeReaderBlockingIO.py| 8 ++ lib/portage/util/futures/_asyncio/__init__.py | 5 +--- 11 files changed, 34 insertions(+), 96 deletions(-) diff --git a/lib/_emerge/AsynchronousLock.py b/lib/_emerge/AsynchronousLock.py index 1a69d0847..c9c8e7f3c 100644 --- a/lib/_emerge/AsynchronousLock.py +++ b/lib/_emerge/AsynchronousLock.py @@ -1,19 +1,10 @@ -# Copyright 2010-2020 Gentoo Authors +# Copyright 2010-2023 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import fcntl import logging import sys - -try: -import dummy_threading -except ImportError: -dummy_threading = None - -try: -import threading -except ImportError: -threading = dummy_threading +import threading import portage from portage import os @@ -39,7 +30,6 @@ class AsynchronousLock(AsynchronousTask): __slots__ = ("path",) + ( "_imp", "_force_async", -"_force_dummy", "_force_process", "_force_thread", "_unlock_future", @@ -61,14 +51,11 @@ class AsynchronousLock(AsynchronousTask): return if self._force_process or ( -not self._force_thread -and (self._use_process_by_default or threading is dummy_threading) +not self._force_thread and self._use_process_by_default ): self._imp = _LockProcess(path=self.path, scheduler=self.scheduler) else: -self._imp = _LockThread( -path=self.path, scheduler=self.scheduler, _force_dummy=self._force_dummy -) +self._imp = _LockThread(path=self.path, scheduler=self.scheduler) self._imp.addExitListener(self._imp_exit) self._imp.start() @@ -115,19 +102,13 @@ class _LockThread(AbstractPollTask): using a background thread. After the lock is acquired, the thread writes to a pipe in order to notify a poll loop running in the main thread. - -If the threading module is unavailable then the dummy_threading -module will be used, and the lock will be acquired synchronously -(before the start() method returns). """ -__slots__ = ("path",) + ("_force_dummy", "_lock_obj", "_thread", "_unlock_future") +__slots__ = ("path",) + ("_lock_obj", "_thread", "_unlock_future") def _start(self): self._registered = True threading_mod = threading -if self._force_dummy: -threading_mod = dummy_threading self._thread = threading_mod.Thread(target=self._run_lock) self._thread.daemon = True self._thread.start() diff --git a/lib/_emerge/PollScheduler.py b/lib/_emerge/PollScheduler.py index 73f2affde..e5dffd8af 100644 --- a/lib/_emerge/PollScheduler.py +++ b/lib/_emerge/PollScheduler.py @@ -1,10 +1,7 @@ -# Copyright 1999-2020 Gentoo Authors +# Copyright 1999-2023 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 -try: -import threading -except ImportError: -import dummy_threading as threading +import threading from portage.util.futures import asyncio from portage.util._async.SchedulerInterface import SchedulerInterface diff --git a/lib/portage/_emirrordist/MirrorDistTask.py b/lib/portage/_emirrordist/MirrorDistTask.py index 5b2543c13..2925394a8 100644 --- a/lib/portage/_emirrordist/MirrorDistTask.py +++ b/lib/portage/_emirrordist/MirrorDistTask.py @@ -4,11 +4,7 @@ import errno import logging import time - -try: -import threading -except ImportError: -import dummy_threading as threading +import threading import portage from portage import os diff --git a/lib/portage/debug.py b/lib/portage/debug.py index b7106b799..ee2dc13e7 100644 --- a/lib/portage/debug.py +++ b/lib/portage/debug.py @@ -1,13 +1,9 @@ -# Copyright
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: 631bedffe29124d693de3b539fc908d9feec1420 Author: Zac Medico gentoo org> AuthorDate: Mon Sep 20 05:05:38 2021 + Commit: Zac Medico gentoo org> CommitDate: Mon Sep 20 05:08:47 2021 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=631bedff _safe_loop: fix python3.10 DeprecationWarning DeprecationWarning: There is no current event loop Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 10 +++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index c1229528a..ccf800c66 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -280,10 +280,14 @@ def _safe_loop(): loop = _thread_weakrefs.loops[thread_key] except KeyError: try: -_real_asyncio.get_event_loop() +try: +_loop = _real_asyncio.get_running_loop() +except AttributeError: +_loop = _real_asyncio.get_event_loop() except RuntimeError: -_real_asyncio.set_event_loop(_real_asyncio.new_event_loop()) -loop = _thread_weakrefs.loops[thread_key] = _AsyncioEventLoop() +_loop = _real_asyncio.new_event_loop() +_real_asyncio.set_event_loop(_loop) +loop = _thread_weakrefs.loops[thread_key] = _AsyncioEventLoop(loop=_loop) if ( _thread_weakrefs.mainloop is None
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: 9eea2af22c9e51475a4adba57fdded3a2a88c886 Author: Zac Medico gentoo org> AuthorDate: Sun Mar 7 14:18:44 2021 + Commit: Zac Medico gentoo org> CommitDate: Sun Mar 7 14:37:46 2021 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=9eea2af2 _writer: Use async and await syntax Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/streams.py | 13 + 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/streams.py b/lib/portage/util/futures/_asyncio/streams.py index ea5882dd3..7a8d4a3e0 100644 --- a/lib/portage/util/futures/_asyncio/streams.py +++ b/lib/portage/util/futures/_asyncio/streams.py @@ -1,4 +1,4 @@ -# Copyright 2018-2020 Gentoo Authors +# Copyright 2018-2021 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import errno @@ -9,7 +9,6 @@ portage.proxy.lazyimport.lazyimport(globals(), '_emerge.PipeReader:PipeReader', 'portage.util.futures:asyncio', ) -from portage.util.futures.compat_coroutine import coroutine def _reader(input_file, loop=None): @@ -55,8 +54,7 @@ class _Reader: self._pipe_reader = None -@coroutine -def _writer(output_file, content, loop=None): +async def _writer(output_file, content, loop=DeprecationWarning): """ Asynchronously write bytes to output file. The output file is assumed to be in non-blocking mode. If an EnvironmentError @@ -68,10 +66,9 @@ def _writer(output_file, content, loop=None): @type output_file: file object @param content: content to write @type content: bytes - @param loop: asyncio.AbstractEventLoop (or compatible) - @type loop: event loop + @param loop: deprecated """ - loop = asyncio._wrap_loop(loop) + loop = asyncio.get_event_loop() fd = output_file.fileno() while content: try: @@ -82,7 +79,7 @@ def _writer(output_file, content, loop=None): waiter = loop.create_future() loop.add_writer(fd, lambda: waiter.done() or waiter.set_result(None)) try: - yield waiter + await waiter finally: # The loop and output file may have been closed. if not loop.is_closed():
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: 15049a041909f85b02a52f5b1938c6dd4171c9e3 Author: Zac Medico gentoo org> AuthorDate: Sun Mar 7 03:03:40 2021 + Commit: Zac Medico gentoo org> CommitDate: Sun Mar 7 05:12:48 2021 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=15049a04 Removed unused portage.util.futures._asyncio.tasks Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 46 ++--- lib/portage/util/futures/_asyncio/tasks.py| 96 --- 2 files changed, 19 insertions(+), 123 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index 207e7205d..4643697e0 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -25,7 +25,16 @@ import types import weakref import asyncio as _real_asyncio -from asyncio.subprocess import Process +# pylint: disable=redefined-builtin +from asyncio import ( + ALL_COMPLETED, + CancelledError, + FIRST_COMPLETED, + FIRST_EXCEPTION, + Future, + InvalidStateError, + TimeoutError, +) try: import threading @@ -38,20 +47,6 @@ portage.proxy.lazyimport.lazyimport(globals(), 'portage.util.futures:compat_coroutine@_compat_coroutine', ) from portage.util._eventloop.asyncio_event_loop import AsyncioEventLoop as _AsyncioEventLoop -# pylint: disable=redefined-builtin -from portage.util.futures.futures import ( - CancelledError, - Future, - InvalidStateError, - TimeoutError, -) -# pylint: enable=redefined-builtin -from portage.util.futures._asyncio.tasks import ( - ALL_COMPLETED, - FIRST_COMPLETED, - FIRST_EXCEPTION, - wait, -) _lock = threading.Lock() @@ -131,20 +126,17 @@ def create_subprocess_exec(*args, **kwargs): # Python 3.4 and later implement PEP 446, which makes newly # created file descriptors non-inheritable by default. kwargs.setdefault('close_fds', False) - if isinstance(loop._asyncio_wrapper, _AsyncioEventLoop): - # Use the real asyncio create_subprocess_exec (loop argument - # is deprecated since since Python 3.8). - return _real_asyncio.create_subprocess_exec(*args, **kwargs) - - result = loop.create_future() + # Use the real asyncio create_subprocess_exec (loop argument + # is deprecated since since Python 3.8). + return ensure_future(_real_asyncio.create_subprocess_exec(*args, **kwargs), loop=loop) - result.set_result(Process(subprocess.Popen( - args, - stdin=kwargs.pop('stdin', None), - stdout=kwargs.pop('stdout', None), - stderr=kwargs.pop('stderr', None), **kwargs), loop)) - return result +def wait(futures, loop=None, timeout=None, return_when=ALL_COMPLETED): + """ + Wraps asyncio.wait() and omits the loop argument which is not + supported since python 3.10. + """ + return _real_asyncio.wait(futures, timeout=timeout, return_when=return_when) def iscoroutinefunction(func): diff --git a/lib/portage/util/futures/_asyncio/tasks.py b/lib/portage/util/futures/_asyncio/tasks.py deleted file mode 100644 index c9db3146e..0 --- a/lib/portage/util/futures/_asyncio/tasks.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright 2018-2020 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -___all___ = ( - 'ALL_COMPLETED', - 'FIRST_COMPLETED', - 'FIRST_EXCEPTION', - 'wait', -) - -from asyncio import ALL_COMPLETED, FIRST_COMPLETED, FIRST_EXCEPTION - -import portage -portage.proxy.lazyimport.lazyimport(globals(), - 'portage.util.futures:asyncio', -) - -def wait(futures, loop=None, timeout=None, return_when=ALL_COMPLETED): - """ - Use portage's internal EventLoop to emulate asyncio.wait: - https://docs.python.org/3/library/asyncio-task.html#asyncio.wait - - @param futures: futures to wait for - @type futures: asyncio.Future (or compatible) - @param timeout: number of seconds to wait (wait indefinitely if - not specified) - @type timeout: int or float - @param return_when: indicates when this function should return, must - be one of the constants ALL_COMPLETED, FIRST_COMPLETED, or - FIRST_EXCEPTION (default is ALL_COMPLETED) - @type return_when: object - @param loop: event loop - @type loop: EventLoop - @return: tuple of (done, pending). - @rtype: asyncio.Future (or compatible) - """ - loop = asyncio._wrap_loop(loop) - result_future = loop.create_future() - _Waiter(futures, timeout, return_when, result_future, loop) - return result_future - - -class _Waiter: - def __init__(self, futures, timeout, return_when, result_future, loop): -
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: 1e843f853a9afe82d599e6ab09064147ddc1d271 Author: Matt Turner gentoo org> AuthorDate: Thu Mar 4 19:24:58 2021 + Commit: Zac Medico gentoo org> CommitDate: Sat Mar 6 09:06:53 2021 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=1e843f85 Use asyncio.subprocess.Process directly With no need to support Python 2, we can remove our private implementation. Signed-off-by: Matt Turner gentoo.org> Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 8 +- lib/portage/util/futures/_asyncio/process.py | 116 -- 2 files changed, 4 insertions(+), 120 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index 5590963f1..207e7205d 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -25,6 +25,7 @@ import types import weakref import asyncio as _real_asyncio +from asyncio.subprocess import Process try: import threading @@ -45,7 +46,6 @@ from portage.util.futures.futures import ( TimeoutError, ) # pylint: enable=redefined-builtin -from portage.util.futures._asyncio.process import _Process from portage.util.futures._asyncio.tasks import ( ALL_COMPLETED, FIRST_COMPLETED, @@ -124,8 +124,8 @@ def create_subprocess_exec(*args, **kwargs): @type loop: event loop @type kwargs: varies @param kwargs: subprocess.Popen parameters - @rtype: asyncio.Future (or compatible) - @return: subset of asyncio.subprocess.Process interface + @rtype: asyncio.subprocess.Process (or compatible) + @return: asyncio.subprocess.Process interface """ loop = _wrap_loop(kwargs.pop('loop', None)) # Python 3.4 and later implement PEP 446, which makes newly @@ -138,7 +138,7 @@ def create_subprocess_exec(*args, **kwargs): result = loop.create_future() - result.set_result(_Process(subprocess.Popen( + result.set_result(Process(subprocess.Popen( args, stdin=kwargs.pop('stdin', None), stdout=kwargs.pop('stdout', None), diff --git a/lib/portage/util/futures/_asyncio/process.py b/lib/portage/util/futures/_asyncio/process.py deleted file mode 100644 index 275c9031a..0 --- a/lib/portage/util/futures/_asyncio/process.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright 2018-2020 Gentoo Authors -# Distributed under the terms of the GNU General Public License v2 - -import os - -import portage -portage.proxy.lazyimport.lazyimport(globals(), - 'portage.util.futures:asyncio', - 'portage.util.futures.unix_events:_set_nonblocking', -) -from portage.util.futures._asyncio.streams import _reader, _writer -from portage.util.futures.compat_coroutine import coroutine, coroutine_return - - -class _Process: - """ - Emulate a subset of the asyncio.subprocess.Process interface, - for python2. - """ - def __init__(self, proc, loop): - """ - @param proc: process instance - @type proc: subprocess.Popen - @param loop: asyncio.AbstractEventLoop (or compatible) - @type loop: event loop - """ - self._proc = proc - self._loop = loop - self.terminate = proc.terminate - self.kill = proc.kill - self.send_signal = proc.send_signal - self.pid = proc.pid - self._waiters = [] - loop._asyncio_child_watcher.\ - add_child_handler(self.pid, self._proc_exit) - - @property - def returncode(self): - return self._proc.returncode - - @coroutine - def communicate(self, input=None, loop=None): # pylint: disable=redefined-builtin - """ - Read data from stdout and stderr, until end-of-file is reached. - Wait for process to terminate. - - @param input: stdin content to write - @type input: bytes - @return: tuple (stdout_data, stderr_data) - @rtype: asyncio.Future (or compatible) - """ - loop = asyncio._wrap_loop(loop or self._loop) - futures = [] - for input_file in (self._proc.stdout, self._proc.stderr): - if input_file is None: - future = loop.create_future() - future.set_result(None) - else: - future = _reader(input_file, loop=loop) - futures.append(future) - - writer = None - if input is not None: - if self._proc.stdin is None: - raise TypeError('communicate: expected file or int, got
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: dcbcac809213537afaa6b4f9822146a2e984f773 Author: Zac Medico gentoo org> AuthorDate: Mon Dec 7 06:05:04 2020 + Commit: Zac Medico gentoo org> CommitDate: Mon Dec 7 07:48:10 2020 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=dcbcac80 _safe_loop: instantiate asyncio loop for API consumer thread In order to maintain compatibility with an API consumer thread which has not instantiated an asyncio loop for the current thread prior to calling the portage API, instantiate a loop on its behalf. Since a ResourceWarning will be triggered if the loop has not been closed before the process exits, add the loop to a WeakValueDictionary, and close it if it still exists during exit for the current pid. Fixes: cecd2f8a259c ("Use default asyncio event loop implementation in API consumer threads") Bug: https://bugs.gentoo.org/758755 Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 49 ++- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index 6f3395a91..d39f31786 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -21,7 +21,8 @@ __all__ = ( ) import subprocess -import sys +import types +import weakref import asyncio as _real_asyncio @@ -249,23 +250,53 @@ def _safe_loop(): the main thread, this returns a globally shared event loop instance. For external API consumers calling from a non-main thread, an - asyncio loop must be registered for the current thread, or else an - error will be raised like this: + asyncio loop must be registered for the current thread, or else the + asyncio.get_event_loop() function will raise an error like this: RuntimeError: There is no current event loop in thread 'Thread-1'. - In order to avoid this RuntimeError, the external API consumer - is responsible for setting an event loop and managing its lifecycle. - For example, this code will set an event loop for the current thread: + In order to avoid this RuntimeError, a loop will be automatically + created like this: asyncio.set_event_loop(asyncio.new_event_loop()) - In order to avoid a ResourceWarning, the caller should also close the - corresponding loop before the current thread terminates. + In order to avoid a ResourceWarning, automatically created loops + are added to a WeakValueDictionary, and closed via an atexit hook + if they still exist during exit for the current pid. @rtype: asyncio.AbstractEventLoop (or compatible) @return: event loop instance """ if portage._internal_caller or threading.current_thread() is threading.main_thread(): return _global_event_loop() - return _AsyncioEventLoop() + + thread_key = threading.get_ident() + with _thread_weakrefs.lock: + if _thread_weakrefs.pid != portage.getpid(): + _thread_weakrefs.pid = portage.getpid() + _thread_weakrefs.loops = weakref.WeakValueDictionary() + try: + loop = _thread_weakrefs.loops[thread_key] + except KeyError: + try: + _real_asyncio.get_event_loop() + except RuntimeError: + _real_asyncio.set_event_loop(_real_asyncio.new_event_loop()) + loop = _thread_weakrefs.loops[thread_key] = _AsyncioEventLoop() + return loop + + +def _thread_weakrefs_atexit(): + with _thread_weakrefs.lock: + if _thread_weakrefs.pid == portage.getpid(): + while True: + try: + thread_key, loop = _thread_weakrefs.loops.popitem() + except KeyError: + break + else: + loop.close() + + +_thread_weakrefs = types.SimpleNamespace(lock=threading.Lock(), loops=None, pid=None) +portage.process.atexit_register(_thread_weakrefs_atexit)
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: d4979a4a2fc6e99a48bd417eae26ce77ae288444 Author: Aaron Bauman gentoo org> AuthorDate: Mon Aug 3 19:06:04 2020 + Commit: Zac Medico gentoo org> CommitDate: Mon Aug 3 19:22:48 2020 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d4979a4a lib/portage/util/futures/_asyncio/tasks.py: drop unused-import * Drop unused import * Update copyright Signed-off-by: Aaron Bauman gentoo.org> Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/tasks.py | 6 +- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/tasks.py b/lib/portage/util/futures/_asyncio/tasks.py index 84c6f4462..c9db3146e 100644 --- a/lib/portage/util/futures/_asyncio/tasks.py +++ b/lib/portage/util/futures/_asyncio/tasks.py @@ -1,4 +1,4 @@ -# Copyright 2018 Gentoo Foundation +# Copyright 2018-2020 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 ___all___ = ( @@ -14,10 +14,6 @@ import portage portage.proxy.lazyimport.lazyimport(globals(), 'portage.util.futures:asyncio', ) -from portage.util._eventloop.global_event_loop import ( - global_event_loop as _global_event_loop, -) - def wait(futures, loop=None, timeout=None, return_when=ALL_COMPLETED): """
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/_sets/, ...
commit: 550727af87d5f646617e0c19a3f3300c8117e7f5 Author: Alec Warner gentoo org> AuthorDate: Fri Jul 24 01:06:54 2020 + Commit: Zac Medico gentoo org> CommitDate: Fri Jul 24 02:10:43 2020 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=550727af Fix redefined-builtin errors for pylint. Some of these are simple variable renames. A few of the uses were refactored and some were simply disabled; often due to redefinition as part of a function signature. I did not do any research in terms of API changes so these were typically left as-is. Closes: https://github.com/gentoo/portage/pull/587 Signed-off-by: Alec Warner gentoo.org> Signed-off-by: Zac Medico gentoo.org> lib/_emerge/actions.py| 2 +- lib/_emerge/depgraph.py | 2 +- lib/_emerge/help.py | 2 +- lib/_emerge/main.py | 2 +- lib/_emerge/resolver/output_helpers.py| 2 +- lib/_emerge/resolver/slot_collision.py| 28 +++ lib/_emerge/search.py | 2 +- lib/portage/_sets/dbapi.py| 8 +++ lib/portage/cache/ebuild_xattr.py | 32 +-- lib/portage/cache/fs_template.py | 4 ++-- lib/portage/cvstree.py| 10 - lib/portage/dbapi/porttree.py | 2 +- lib/portage/glsa.py | 4 ++-- lib/portage/manifest.py | 4 ++-- lib/portage/repository/config.py | 8 +++ lib/portage/tests/bin/setup_env.py| 18 +++ lib/portage/tests/sets/shell/testShell.py | 6 ++--- lib/portage/util/_urlopen.py | 3 +-- lib/portage/util/futures/_asyncio/__init__.py | 2 ++ lib/portage/util/futures/_asyncio/process.py | 2 +- lib/portage/util/futures/events.py| 2 +- lib/portage/util/futures/futures.py | 2 ++ lib/portage/util/lafilefixer.py | 8 +++ 23 files changed, 79 insertions(+), 76 deletions(-) diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py index 964dca31c..2cbca99d8 100644 --- a/lib/_emerge/actions.py +++ b/lib/_emerge/actions.py @@ -27,7 +27,7 @@ portage.proxy.lazyimport.lazyimport(globals(), 'portage.util.locale:check_locale', 'portage.emaint.modules.sync.sync:SyncRepos', '_emerge.chk_updated_cfg_files:chk_updated_cfg_files', - '_emerge.help:help@emerge_help', + '_emerge.help:emerge_help', '_emerge.post_emerge:display_news_notification,post_emerge', '_emerge.stdout_spinner:stdout_spinner', ) diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py index 653348d34..3ff90190d 100644 --- a/lib/_emerge/depgraph.py +++ b/lib/_emerge/depgraph.py @@ -7476,7 +7476,7 @@ class depgraph: mygraph.order.sort(key=cmp_sort_key(cmp_merge_preference)) - def altlist(self, reversed=DeprecationWarning): + def altlist(self, reversed=DeprecationWarning): # pylint: disable=redefined-builtin if reversed is not DeprecationWarning: warnings.warn("The reversed parameter of " diff --git a/lib/_emerge/help.py b/lib/_emerge/help.py index 2ccd323aa..de3d7b593 100644 --- a/lib/_emerge/help.py +++ b/lib/_emerge/help.py @@ -5,7 +5,7 @@ from __future__ import print_function from portage.output import bold, turquoise, green -def help(): +def emerge_help(): print(bold("emerge:")+" command-line interface to the Portage system") print(bold("Usage:")) print(" "+turquoise("emerge")+" [ "+green("options")+" ] [ "+green("action")+" ] [ "+turquoise("ebuild")+" | "+turquoise("tbz2")+" | "+turquoise("file")+" | "+turquoise("@set")+" | "+turquoise("atom")+" ] [ ... ]") diff --git a/lib/_emerge/main.py b/lib/_emerge/main.py index 69d93f846..8228e0b41 100644 --- a/lib/_emerge/main.py +++ b/lib/_emerge/main.py @@ -16,7 +16,7 @@ portage.proxy.lazyimport.lazyimport(globals(), 'textwrap', '_emerge.actions:load_emerge_config,run_action,' + \ 'validate_ebuild_environment', - '_emerge.help:help@emerge_help', + '_emerge.help:emerge_help', '_emerge.is_valid_package_atom:insert_category_into_atom' ) from portage import os diff --git a/lib/_emerge/resolver/output_helpers.py b/lib/_emerge/resolver/output_helpers.py index d5cc9dbcb..25aa925b4 100644 --- a/lib/_emerge/resolver/output_helpers.py +++ b/lib/_emerge/resolver/output_helpers.py @@ -469,7 +469,7 @@ def _prune_tree_display(display_list): del display_list[i] -def _calc_changelog(ebuildpath,current,next): +def _calc_changelog(ebuildpath,current,next): # pylint: disable=redefined-builtin if ebuildpath == None or not os.path.exists(ebuildpath): return [] current = '-'.join(catpkgsplit(current)[1:]) diff --git
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: 92be5a02e452eb0810d2974bc7fa5ee2056ef8e7 Author: Zac Medico gentoo org> AuthorDate: Thu Jun 18 05:33:26 2020 + Commit: Zac Medico gentoo org> CommitDate: Thu Jun 18 17:54:37 2020 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=92be5a02 _writer: fix unsafe finally clause (bug 728580) In the coroutine finally clause, do not call remove_writer in cases where fd has been closed and then re-allocated to a concurrent coroutine as in bug 716636. Also, assume that the caller will put the file in non-blocking mode and close the file when done, so that this function is suitable for use within a loop. Bug: https://bugs.gentoo.org/728580 Reviewed-by: Brian Dolbec gentoo.org> Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/process.py | 11 -- lib/portage/util/futures/_asyncio/streams.py | 50 +--- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/process.py b/lib/portage/util/futures/_asyncio/process.py index 020164c9b..2d3e9b0fd 100644 --- a/lib/portage/util/futures/_asyncio/process.py +++ b/lib/portage/util/futures/_asyncio/process.py @@ -1,9 +1,12 @@ -# Copyright 2018 Gentoo Foundation +# Copyright 2018-2020 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 +import os + import portage portage.proxy.lazyimport.lazyimport(globals(), 'portage.util.futures:asyncio', + 'portage.util.futures.unix_events:_set_nonblocking', ) from portage.util.futures._asyncio.streams import _reader, _writer from portage.util.futures.compat_coroutine import coroutine, coroutine_return @@ -59,7 +62,11 @@ class _Process(object): if input is not None: if self._proc.stdin is None: raise TypeError('communicate: expected file or int, got {}'.format(type(self._proc.stdin))) - writer = asyncio.ensure_future(_writer(self._proc.stdin, input), loop=self._loop) + stdin = self._proc.stdin + stdin = os.fdopen(stdin, 'wb', 0) if isinstance(stdin, int) else stdin + _set_nonblocking(stdin.fileno()) + writer = asyncio.ensure_future(_writer(stdin, input, loop=self._loop), loop=self._loop) + writer.add_done_callback(lambda writer: stdin.close()) try: yield asyncio.wait(futures + [self.wait()], loop=self._loop) diff --git a/lib/portage/util/futures/_asyncio/streams.py b/lib/portage/util/futures/_asyncio/streams.py index 650a16491..870307e1e 100644 --- a/lib/portage/util/futures/_asyncio/streams.py +++ b/lib/portage/util/futures/_asyncio/streams.py @@ -1,4 +1,4 @@ -# Copyright 2018 Gentoo Foundation +# Copyright 2018-2020 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import errno @@ -8,7 +8,6 @@ import portage portage.proxy.lazyimport.lazyimport(globals(), '_emerge.PipeReader:PipeReader', 'portage.util.futures:asyncio', - 'portage.util.futures.unix_events:_set_nonblocking', ) from portage.util.futures.compat_coroutine import coroutine @@ -59,38 +58,37 @@ class _Reader(object): @coroutine def _writer(output_file, content, loop=None): """ - Asynchronously write bytes to output file, and close it when - done. If an EnvironmentError other than EAGAIN is encountered, - which typically indicates that the other end of the pipe has - close, the error is raised. This function is a coroutine. + Asynchronously write bytes to output file. The output file is + assumed to be in non-blocking mode. If an EnvironmentError + other than EAGAIN is encountered, which typically indicates that + the other end of the pipe has closed, the error is raised. + This function is a coroutine. - @param output_file: output file descriptor - @type output_file: file or int + @param output_file: output file + @type output_file: file object @param content: content to write @type content: bytes @param loop: asyncio.AbstractEventLoop (or compatible) @type loop: event loop """ - fd = output_file if isinstance(output_file, int) else output_file.fileno() - _set_nonblocking(fd) loop = asyncio._wrap_loop(loop) - try: - while content: + fd = output_file.fileno() + while content: + try: + content = content[os.write(fd, content):] + except EnvironmentError as e: + if e.errno != errno.EAGAIN: + raise waiter = loop.create_future() - loop.add_writer(fd, lambda: waiter.set_result(None)) + loop.add_writer(fd, lambda: waiter.done() or
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: 64b11fe4dbcd7f2b4c36d8c40a09425a2c624c7a Author: Zac Medico gentoo org> AuthorDate: Sat Feb 29 04:24:19 2020 + Commit: Zac Medico gentoo org> CommitDate: Sat Feb 29 04:31:50 2020 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=64b11fe4 asyncio: improve _AsyncioEventLoop isinstance logic Since _AsyncioEventLoop can be wrapped, use the _asyncio_wrapper attributre for isinstance checks (_wrap_loop guarantees that this attribute exists). Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index 7635dbb5e..f4b03891f 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2018 Gentoo Foundation +# Copyright 2018-2020 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 __all__ = ( @@ -139,7 +139,7 @@ def create_subprocess_exec(*args, **kwargs): """ loop = _wrap_loop(kwargs.pop('loop', None)) kwargs.setdefault('close_fds', _close_fds_default) - if _asyncio_enabled and isinstance(loop, _AsyncioEventLoop): + if _asyncio_enabled and isinstance(loop._asyncio_wrapper, _AsyncioEventLoop): # Use the real asyncio create_subprocess_exec (loop argument # is deprecated since since Python 3.8). return _real_asyncio.create_subprocess_exec(*args, **kwargs) @@ -191,10 +191,10 @@ def ensure_future(coro_or_future, loop=None): @return: an instance of Future """ loop = _wrap_loop(loop) - if _asyncio_enabled and isinstance(loop, _AsyncioEventLoop): + if _asyncio_enabled and isinstance(loop._asyncio_wrapper, _AsyncioEventLoop): # Use the real asyncio loop and ensure_future. return _real_asyncio.ensure_future( - coro_or_future, loop=loop._loop) + coro_or_future, loop=loop._asyncio_wrapper._loop) if isinstance(coro_or_future, Future): return coro_or_future
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: 67e0bba35f1b7afd5ee5ca648154838741875d6a Author: Zac Medico gentoo org> AuthorDate: Fri Oct 18 03:35:46 2019 + Commit: Zac Medico gentoo org> CommitDate: Fri Oct 18 03:37:40 2019 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=67e0bba3 asyncio.create_subprocess_exec: suppress DeprecationWarning for loop argument See: https://bugs.python.org/issue36373 Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index bd9f2e47c..7635dbb5e 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -140,8 +140,9 @@ def create_subprocess_exec(*args, **kwargs): loop = _wrap_loop(kwargs.pop('loop', None)) kwargs.setdefault('close_fds', _close_fds_default) if _asyncio_enabled and isinstance(loop, _AsyncioEventLoop): - # Use the real asyncio loop and create_subprocess_exec. - return _real_asyncio.create_subprocess_exec(*args, loop=loop._loop, **kwargs) + # Use the real asyncio create_subprocess_exec (loop argument + # is deprecated since since Python 3.8). + return _real_asyncio.create_subprocess_exec(*args, **kwargs) result = loop.create_future()
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/
commit: cef213c31ce05920fd1b8bbf2749f012944f0c2a Author: Zac Medico gentoo org> AuthorDate: Sat May 18 22:16:13 2019 + Commit: Zac Medico gentoo org> CommitDate: Sat May 18 22:20:50 2019 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=cef213c3 _safe_loop: call global_event_loop() directly Since _wrap_loop() always returns an identical result in this context, call global_event_loop() directly. Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index e77c7a690..bd9f2e47c 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -265,6 +265,6 @@ def _safe_loop(): @return: event loop instance """ if portage._internal_caller: - return _wrap_loop() + return _global_event_loop() else: return _EventLoop(main=False)
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/util/futures/
commit: 48c06e489e695321e8059da2dac1c03f6624d2e8 Author: Zac Medico gentoo org> AuthorDate: Mon Aug 6 06:43:41 2018 + Commit: Zac Medico gentoo org> CommitDate: Mon Sep 24 03:41:17 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=48c06e48 Implement asyncio.iscoroutinefunction for compat_coroutine Sometimes it's useful to test if a function is a coroutine function, so implement a version of asyncio.iscoroutinefunction that works with asyncio.coroutine as well as compat_coroutine.coroutine (since both kinds of coroutine functions behave identically for our purposes). Reviewed-by: Brian Dolbec gentoo.org> Signed-off-by: Zac Medico gentoo.org> lib/portage/util/futures/_asyncio/__init__.py | 14 ++ lib/portage/util/futures/compat_coroutine.py | 12 2 files changed, 26 insertions(+) diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py index faab98e47..2a637624d 100644 --- a/lib/portage/util/futures/_asyncio/__init__.py +++ b/lib/portage/util/futures/_asyncio/__init__.py @@ -36,6 +36,7 @@ except ImportError: import portage portage.proxy.lazyimport.lazyimport(globals(), 'portage.util.futures.unix_events:_PortageEventLoopPolicy', + 'portage.util.futures:compat_coroutine@_compat_coroutine', ) from portage.util._eventloop.asyncio_event_loop import AsyncioEventLoop as _AsyncioEventLoop from portage.util._eventloop.global_event_loop import ( @@ -152,6 +153,19 @@ def create_subprocess_exec(*args, **kwargs): return result +def iscoroutinefunction(func): + """ + Return True if func is a decorated coroutine function, + supporting both asyncio.coroutine and compat_coroutine since + their behavior is identical for all practical purposes. + """ + if _compat_coroutine._iscoroutinefunction(func): + return True + elif _real_asyncio is not None and _real_asyncio.iscoroutinefunction(func): + return True + return False + + class Task(Future): """ Schedule the execution of a coroutine: wrap it in a future. A task diff --git a/lib/portage/util/futures/compat_coroutine.py b/lib/portage/util/futures/compat_coroutine.py index 3edfa6bee..b5ff92faf 100644 --- a/lib/portage/util/futures/compat_coroutine.py +++ b/lib/portage/util/futures/compat_coroutine.py @@ -8,6 +8,17 @@ portage.proxy.lazyimport.lazyimport(globals(), 'portage.util.futures:asyncio', ) +# A marker for iscoroutinefunction. +_is_coroutine = object() + + +def _iscoroutinefunction(func): + """ + Return True if func is a decorated coroutine function + created with the coroutine decorator for this module. + """ + return getattr(func, '_is_coroutine', None) is _is_coroutine + def coroutine(generator_func): """ @@ -34,6 +45,7 @@ def coroutine(generator_func): @functools.wraps(generator_func) def wrapped(*args, **kwargs): return _generator_future(generator_func, *args, **kwargs) + wrapped._is_coroutine = _is_coroutine return wrapped
[gentoo-commits] proj/portage:master commit in: lib/portage/util/futures/_asyncio/, lib/portage/util/futures/, ...
commit: 6b4252d3a0f12808a5bcce888b7f68e1f84b5301 Author: Zac Medico gentoo org> AuthorDate: Sat Jul 28 21:22:42 2018 + Commit: Zac Medico gentoo org> CommitDate: Mon Aug 6 04:38:41 2018 + URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=6b4252d3 Add asyncio.create_subprocess_exec support for python2 (bug 662388) The asyncio.create_subprocess_exec function is essential for using subprocesses in coroutines, so add support to do this for python2. This paves the way for extensive use of coroutines in portage, since coroutines are well-suited for many portage tasks that involve subprocesses. Bug: https://bugs.gentoo.org/662388 .../util/futures/asyncio/test_subprocess_exec.py | 184 ++--- lib/portage/util/futures/_asyncio/__init__.py | 53 ++ lib/portage/util/futures/_asyncio/process.py | 107 lib/portage/util/futures/_asyncio/streams.py | 96 +++ lib/portage/util/futures/compat_coroutine.py | 6 +- 5 files changed, 315 insertions(+), 131 deletions(-) diff --git a/lib/portage/tests/util/futures/asyncio/test_subprocess_exec.py b/lib/portage/tests/util/futures/asyncio/test_subprocess_exec.py index 5a812ba6a..61646cb92 100644 --- a/lib/portage/tests/util/futures/asyncio/test_subprocess_exec.py +++ b/lib/portage/tests/util/futures/asyncio/test_subprocess_exec.py @@ -3,61 +3,16 @@ import os import subprocess - -try: - from asyncio import create_subprocess_exec -except ImportError: - create_subprocess_exec = None +import sys 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._asyncio import create_subprocess_exec +from portage.util.futures._asyncio.streams import _reader as reader +from portage.util.futures.compat_coroutine import coroutine, coroutine_return from portage.util.futures.unix_events import DefaultEventLoopPolicy -from _emerge.PipeReader import PipeReader - - -def reader(input_file, loop=None): - """ - Asynchronously read a binary input file. - - @param input_file: binary input file - @type input_file: file - @param loop: event loop - @type loop: EventLoop - @return: bytes - @rtype: asyncio.Future (or compatible) - """ - loop = asyncio._wrap_loop(loop) - future = loop.create_future() - _Reader(future, input_file, loop) - return future - - -class _Reader(object): - def __init__(self, future, input_file, loop): - self._future = future - self._pipe_reader = PipeReader( - input_files={'input_file':input_file}, scheduler=loop) - - self._future.add_done_callback(self._cancel_callback) - self._pipe_reader.addExitListener(self._eof) - self._pipe_reader.start() - - def _cancel_callback(self, future): - if future.cancelled(): - self._cancel() - - def _eof(self, pipe_reader): - self._pipe_reader = None - self._future.set_result(pipe_reader.getvalue()) - - def _cancel(self): - if self._pipe_reader is not None and self._pipe_reader.poll() is None: - self._pipe_reader.removeExitListener(self._eof) - self._pipe_reader.cancel() - self._pipe_reader = None class SubprocessExecTestCase(TestCase): @@ -76,99 +31,66 @@ class SubprocessExecTestCase(TestCase): self.assertFalse(global_event_loop().is_closed()) def testEcho(self): - if create_subprocess_exec is None: - self.skipTest('create_subprocess_exec not implemented for python2') - args_tuple = (b'hello', b'world') echo_binary = find_binary("echo") self.assertNotEqual(echo_binary, None) echo_binary = echo_binary.encode() - # Use os.pipe(), since this loop does not implement the - # ReadTransport necessary for subprocess.PIPE support. - stdout_pr, stdout_pw = os.pipe() - stdout_pr = os.fdopen(stdout_pr, 'rb', 0) - stdout_pw = os.fdopen(stdout_pw, 'wb', 0) - files = [stdout_pr, stdout_pw] - def test(loop): - output = None - try: - with open(os.devnull, 'rb', 0) as devnull: - proc = loop.run_until_complete( - create_subprocess_exec( - echo_binary, *args_tuple, - stdin=devnull,