Add a portdbapi.async_fetch_map method which is identical to the existing portdbapi.getFetchMap method, but returns a future. This will be used by EbuildFetcher in order to avoid event loop recursion.
Bug: https://bugs.gentoo.org/653810 --- pym/portage/dbapi/porttree.py | 75 +++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/pym/portage/dbapi/porttree.py b/pym/portage/dbapi/porttree.py index 951e5760a..3cd929963 100644 --- a/pym/portage/dbapi/porttree.py +++ b/pym/portage/dbapi/porttree.py @@ -728,25 +728,66 @@ class portdbapi(dbapi): URIs. @rtype: dict """ + loop = self._event_loop + return loop.run_until_complete( + self.async_fetch_map(mypkg, useflags=useflags, + mytree=mytree, loop=loop)) - try: - eapi, myuris = self.aux_get(mypkg, - ["EAPI", "SRC_URI"], mytree=mytree) - except KeyError: - # Convert this to an InvalidDependString exception since callers - # already handle it. - raise portage.exception.InvalidDependString( - "getFetchMap(): aux_get() error reading "+mypkg+"; aborting.") + def async_fetch_map(self, mypkg, useflags=None, mytree=None, loop=None): + """ + Asynchronous form of getFetchMap. - if not eapi_is_supported(eapi): - # Convert this to an InvalidDependString exception - # since callers already handle it. - raise portage.exception.InvalidDependString( - "getFetchMap(): '%s' has unsupported EAPI: '%s'" % \ - (mypkg, eapi)) - - return _parse_uri_map(mypkg, {'EAPI':eapi,'SRC_URI':myuris}, - use=useflags) + @param mypkg: cpv for an ebuild + @type mypkg: String + @param useflags: a collection of enabled USE flags, for evaluation of + conditionals + @type useflags: set, or None to enable all conditionals + @param mytree: The canonical path of the tree in which the ebuild + is located, or None for automatic lookup + @type mypkg: String + @param loop: event loop (defaults to global event loop) + @type loop: EventLoop + @return: A future that results in a dict which maps each file name to + a set of alternative URIs. + @rtype: asyncio.Future (or compatible) + """ + loop = loop or global_event_loop() + loop = getattr(loop, '_asyncio_wrapper', loop) + result = loop.create_future() + + def aux_get_done(aux_get_future): + if result.cancelled(): + return + if aux_get_future.exception() is not None: + if isinstance(future.exception(), PortageKeyError): + # Convert this to an InvalidDependString exception since + # callers already handle it. + result.set_exception(portage.exception.InvalidDependString( + "getFetchMap(): aux_get() error reading " + + mypkg + "; aborting.")) + else: + result.set_exception(future.exception()) + return + + eapi, myuris = aux_get_future.result() + + if not eapi_is_supported(eapi): + # Convert this to an InvalidDependString exception + # since callers already handle it. + result.set_exception(portage.exception.InvalidDependString( + "getFetchMap(): '%s' has unsupported EAPI: '%s'" % \ + (mypkg, eapi))) + return + + result.set_result(_parse_uri_map(mypkg, + {'EAPI':eapi,'SRC_URI':myuris}, use=useflags)) + + aux_get_future = self.async_aux_get( + mypkg, ["EAPI", "SRC_URI"], mytree=mytree) + result.add_done_callback(lambda result: + aux_get_future.cancel() if result.cancelled() else None) + aux_get_future.add_done_callback(aux_get_done) + return result def getfetchsizes(self, mypkg, useflags=None, debug=0, myrepo=None): # returns a filename:size dictionnary of remaining downloads -- 2.13.6