commit:     16655f8588e331766944d61402736be381001aab
Author:     Florian Schmaus <flow <AT> gentoo <DOT> org>
AuthorDate: Tue Oct 28 08:05:30 2025 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Sat Nov  1 09:48:56 2025 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=16655f85

bintree: fix AttributeError by swapping try and for-loop

Swap the try-block and for-loop in _populate_remote().

Instead of the try-block being within the for-loop, the try-block is
no the outermost block containing the for-loop. This is sensible
because previously the beginning of the for-loop's body contained some
logic that was unrelated to the for-loop, i.e., the checks if the repo
is frozen or if the currently cached index is within its TTL.

This also fixes an AttributeError where in
    ttl = float(pkgindex.header.get("TTL", 0))
where pkgindex would be None in a subsequent iteration of the
loop. Now this code resides outside the for-loop (where it belongs).

This fixes the issue initially reported in

    https://bugs.gentoo.org/965098#c0

Furthermore, we move the finally block where we kill the download proc
and unlink the tmp file, which was previously attached to the
outermost try-block, to the innermost try-block because it needs to be
executed on every download attempt.

Bug: https://bugs.gentoo.org/965098
Signed-off-by: Florian Schmaus <flow <AT> gentoo.org>
Part-of: https://github.com/gentoo/portage/pull/1490
Signed-off-by: Sam James <sam <AT> gentoo.org>

 lib/portage/dbapi/bintree.py | 121 ++++++++++++++++++++-----------------------
 1 file changed, 57 insertions(+), 64 deletions(-)

diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py
index ebd0ec08c0..55c1298705 100644
--- a/lib/portage/dbapi/bintree.py
+++ b/lib/portage/dbapi/bintree.py
@@ -1419,31 +1419,31 @@ class binarytree:
             rmt_idx = self._new_pkgindex()
             proc = None
             tmp_filename = None
-            for remote_pkgindex_file in ("Packages.gz", "Packages"):
+            try:
+                if local_timestamp and (repo.frozen or not getbinpkg_refresh):
+                    if repo.frozen:
+                        raise UseCachedCopyOfRemoteIndex("frozen")
+                    raise UseCachedCopyOfRemoteIndex("")
+
                 try:
+                    ttl = float(pkgindex.header.get("TTL", 0))
+                except ValueError:
+                    pass
+                else:
+                    if (
+                        download_timestamp
+                        and ttl
+                        and download_timestamp + ttl > time.time()
+                    ):
+                        raise UseCachedCopyOfRemoteIndex("within TTL")
+
+                for remote_pkgindex_file in ("Packages.gz", "Packages"):
                     # urlparse.urljoin() only works correctly with recognized
                     # protocols and requires the base url to have a trailing
                     # slash, so join manually...
                     url = base_url.rstrip("/") + "/" + remote_pkgindex_file
                     f = None
 
-                    if local_timestamp and (repo.frozen or not 
getbinpkg_refresh):
-                        if repo.frozen:
-                            raise UseCachedCopyOfRemoteIndex("frozen")
-                        raise UseCachedCopyOfRemoteIndex("")
-
-                    try:
-                        ttl = float(pkgindex.header.get("TTL", 0))
-                    except ValueError:
-                        pass
-                    else:
-                        if (
-                            download_timestamp
-                            and ttl
-                            and download_timestamp + ttl > time.time()
-                        ):
-                            raise UseCachedCopyOfRemoteIndex("within TTL")
-
                     # Set proxy settings for _urlopen -> urllib_request
                     proxies = {}
                     for proto in ("http", "https"):
@@ -1519,6 +1519,28 @@ class binarytree:
                                 raise UseCachedCopyOfRemoteIndex(
                                     "up-to-date", extra_info
                                 )
+                            if (
+                                remote_pkgindex_file == "Packages.gz"
+                                and isinstance(err, urllib.error.HTTPError)
+                                and err.code == 404
+                            ):
+                                # Ignore 404s for Packages.gz, as the file is
+                                # not guaranteed to exist.
+                                continue
+
+                            # This includes URLError which is raised for SSL
+                            # certificate errors when PEP 476 is supported.
+                            writemsg(
+                                _(
+                                    "\n\n!!! [%s] Error fetching binhost 
package"
+                                    " info from '%s'\n"
+                                )
+                                % (binrepo_name, _hide_url_passwd(base_url))
+                            )
+                            error_msg = str(err)
+                            writemsg(f"!!!{binrepo_name} {error_msg}\n\n")
+                            del err
+                            pkgindex = None
 
                             if parsed_url.scheme in ("ftp", "http", "https"):
                                 # This protocol is supposedly supported by 
urlopen,
@@ -1654,57 +1676,28 @@ class binarytree:
                                 ),
                                 noiselevel=-1,
                             )
+                        if proc is not None:
+                            if proc.poll() is None:
+                                proc.kill()
+                                proc.wait()
+                            proc = None
+                        if tmp_filename is not None:
+                            try:
+                                os.unlink(tmp_filename)
+                            except OSError:
+                                pass
                     # We successfully fetched the remote index, break
                     # out of the ("Packages.gz", "Packages") loop.
                     break
-                except UseCachedCopyOfRemoteIndex as exc:
-                    changed = False
-                    rmt_idx = pkgindex
-                    if getbinpkg_refresh or repo.frozen:
-                        extra_info = exc.extra_info if verbose else ""
-                        writemsg(
-                            _(
-                                "[%s] Local copy of remote index is %s and 
will be used%s.\n"
-                            )
-                            % (binrepo_name, exc.desc, extra_info),
-                        )
-                    # We are using the cached index, break out of the
-                    # ("Packages.gz", "Packages") loop.
-                    break
-                except OSError as e:
-                    if (
-                        remote_pkgindex_file == "Packages.gz"
-                        and isinstance(e, urllib.error.HTTPError)
-                        and e.code == 404
-                    ):
-                        # Ignore 404s for Packages.gz, as the file is
-                        # not guaranteed to exist.
-                        continue
-
-                    # This includes URLError which is raised for SSL
-                    # certificate errors when PEP 476 is supported.
+            except UseCachedCopyOfRemoteIndex as exc:
+                changed = False
+                rmt_idx = pkgindex
+                if getbinpkg_refresh or repo.frozen:
+                    extra_info = exc.extra_info if verbose else ""
                     writemsg(
-                        _(
-                            "\n\n!!! [%s] Error fetching binhost package"
-                            " info from '%s'\n"
-                        )
-                        % (binrepo_name, _hide_url_passwd(base_url))
+                        _("[%s] Local copy of remote index is %s and will be 
used%s.\n")
+                        % (binrepo_name, exc.desc, extra_info),
                     )
-                    error_msg = str(e)
-                    writemsg(f"!!!{binrepo_name} {error_msg}\n\n")
-                    del e
-                    pkgindex = None
-                finally:
-                    if proc is not None:
-                        if proc.poll() is None:
-                            proc.kill()
-                            proc.wait()
-                        proc = None
-                    if tmp_filename is not None:
-                        try:
-                            os.unlink(tmp_filename)
-                        except OSError:
-                            pass
 
             if pkgindex is rmt_idx and changed:
                 pkgindex.modified = False  # don't update the header

Reply via email to