Add async_aux_get method that returns a Future and otherwise behaves identically to aux_get. Use async_aux_get to implement the synchronous aux_get method.
Bug: https://bugs.gentoo.org/648790 --- pym/portage/dbapi/porttree.py | 91 +++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 21 deletions(-) diff --git a/pym/portage/dbapi/porttree.py b/pym/portage/dbapi/porttree.py index f5979d2d0..abcd47238 100644 --- a/pym/portage/dbapi/porttree.py +++ b/pym/portage/dbapi/porttree.py @@ -45,6 +45,7 @@ import traceback import warnings import errno import collections +import functools try: from urllib.parse import urlparse @@ -577,11 +578,46 @@ class portdbapi(dbapi): "stub code for returning auxilliary db information, such as SLOT, DEPEND, etc." 'input: "sys-apps/foo-1.0",["SLOT","DEPEND","HOMEPAGE"]' 'return: ["0",">=sys-libs/bar-1.0","http://www.foo.com"] or raise PortageKeyError if error' + # For external API consumers, self._event_loop returns a new event + # loop on each access, so a local reference is needed in order + # to avoid instantiating more than one. + loop = self._event_loop + return loop.run_until_complete( + self.async_aux_get(mycpv, mylist, mytree=mytree, + myrepo=myrepo, loop=loop)) + + def async_aux_get(self, mycpv, mylist, mytree=None, myrepo=None, loop=None): + """ + Asynchronous form form of aux_get. + + @param mycpv: cpv for an ebuild + @type mycpv: str + @param mylist: list of metadata keys + @type mylist: list + @param mytree: The canonical path of the tree in which the ebuild + is located, or None for automatic lookup + @type mytree: str + @param myrepo: name of the repo in which the ebuild is located, + or None for automatic lookup + @type myrepo: str + @param loop: event loop (defaults to global event loop) + @type loop: EventLoop + @return: list of metadata values + @rtype: asyncio.Future (or compatible) + """ + # Don't default to self._event_loop here, since that creates a + # local event loop for thread safety, and that could easily lead + # to simultaneous instantiation of multiple event loops here. + # Callers of this method certainly want the same event loop to + # be used for all calls. + loop = loop or global_event_loop() + future = loop.create_future() cache_me = False if myrepo is not None: mytree = self.treemap.get(myrepo) if mytree is None: - raise PortageKeyError(myrepo) + future.set_exception(PortageKeyError(myrepo)) + return future if mytree is not None and len(self.porttrees) == 1 \ and mytree == self.porttrees[0]: @@ -596,43 +632,56 @@ class portdbapi(dbapi): mylist).difference(self._aux_cache_keys): aux_cache = self._aux_cache.get(mycpv) if aux_cache is not None: - return [aux_cache.get(x, "") for x in mylist] + future.set_result([aux_cache.get(x, "") for x in mylist]) + return future cache_me = True try: cat, pkg = mycpv.split("/", 1) except ValueError: # Missing slash. Can't find ebuild so raise PortageKeyError. - raise PortageKeyError(mycpv) + future.set_exception(PortageKeyError(mycpv)) + return future myebuild, mylocation = self.findname2(mycpv, mytree) if not myebuild: writemsg("!!! aux_get(): %s\n" % \ _("ebuild not found for '%s'") % mycpv, noiselevel=1) - raise PortageKeyError(mycpv) + future.set_exception(PortageKeyError(mycpv)) + return future mydata, ebuild_hash = self._pull_valid_cache(mycpv, myebuild, mylocation) - doregen = mydata is None - - if doregen: - if myebuild in self._broken_ebuilds: - raise PortageKeyError(mycpv) - - proc = EbuildMetadataPhase(cpv=mycpv, - ebuild_hash=ebuild_hash, portdb=self, - repo_path=mylocation, scheduler=self._event_loop, - settings=self.doebuild_settings) - - proc.start() - proc.wait() + if mydata is not None: + self._aux_get_return( + future, mycpv, mylist, myebuild, ebuild_hash, + mydata, mylocation, cache_me, None) + return future + + if myebuild in self._broken_ebuilds: + future.set_exception(PortageKeyError(mycpv)) + return future + + proc = EbuildMetadataPhase(cpv=mycpv, + ebuild_hash=ebuild_hash, portdb=self, + repo_path=mylocation, scheduler=loop, + settings=self.doebuild_settings) + + proc.addExitListener(functools.partial(self._aux_get_return, + future, mycpv, mylist, myebuild, ebuild_hash, mydata, mylocation, + cache_me)) + proc.start() + return future + + def _aux_get_return(self, future, mycpv, mylist, myebuild, ebuild_hash, + mydata, mylocation, cache_me, proc): + if proc is not None: if proc.returncode != os.EX_OK: self._broken_ebuilds.add(myebuild) - raise PortageKeyError(mycpv) - + future.set_exception(PortageKeyError(mycpv)) + return mydata = proc.metadata - mydata["repository"] = self.repositories.get_name_for_location(mylocation) mydata["_mtime_"] = ebuild_hash.mtime eapi = mydata.get("EAPI") @@ -651,7 +700,7 @@ class portdbapi(dbapi): aux_cache[x] = mydata.get(x, "") self._aux_cache[mycpv] = aux_cache - return returnme + future.set_result(returnme) def getFetchMap(self, mypkg, useflags=None, mytree=None): """ -- 2.13.6